source: src/mlx/pirep.py@ 854:3e7dca86c1ed

Last change on this file since 854:3e7dca86c1ed was 854:3e7dca86c1ed, checked in by István Váradi <ivaradi@…>, 7 years ago

The accepted flights can be queried and viewed (re #307)

File size: 13.4 KB
Line 
1
2from util import utf2unicode
3from flight import Flight
4
5import const
6import cPickle as pickle
7import calendar
8import datetime
9import time
10
11#------------------------------------------------------------------------------
12
13## @package mlx.pirep
14#
15# The PIREP module.
16#
17# This module defines only one class, \ref PIREP. It is used to extract and
18# store the information needed for a PIREP. The saved PIREPs are pickled
19# instances of this class.
20
21#------------------------------------------------------------------------------
22
23class PIREP(object):
24 """A pilot's report of a flight."""
25 _flightTypes = { const.FLIGHTTYPE_SCHEDULED : "SCHEDULED",
26 const.FLIGHTTYPE_OLDTIMER : "OT",
27 const.FLIGHTTYPE_VIP : "VIP",
28 const.FLIGHTTYPE_CHARTER : "CHARTER" }
29
30 @staticmethod
31 def _formatLine(timeStr, line):
32 """Format the given time string and line as needed for the ACARS and
33 some other things."""
34 return "[" + timeStr + "]-[" + line + "]"
35
36 @staticmethod
37 def formatTimestampForRPC(t):
38 """Format the given timestamp for RPC."""
39 return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(t))
40
41 @staticmethod
42 def parseTimestampFromRPC(s):
43 """Format the given timestamp for RPC."""
44 dt = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
45 return calendar.timegm(dt.utctimetuple())
46
47 @staticmethod
48 def decodeFlightTypeText(s):
49 """Decode the given flight type text."""
50 for (flighType, text) in PIREP._flightTypes.iteritems():
51 if s==text:
52 return flighType
53 return const.FLIGHTYPE_SCHEDULED
54
55 @staticmethod
56 def parseLogFromRPC(log):
57 """Parse the given log coming from the RPC."""
58 index = 0
59 entries = []
60
61 inTimeStr = False
62 inEntry = False
63
64 timestr = ""
65 entry = ""
66
67 while index<len(log):
68 c = log[index]
69 index += 1
70
71 if c==']':
72 if inEntry:
73 entries.append((timestr, entry))
74 timestr = ""
75 entry = ""
76
77 inTimeStr = False
78 inEntry = False
79 elif not inTimeStr and not inEntry:
80 if c=='[':
81 if timestr:
82 inEntry = True
83 else:
84 inTimeStr = True
85 elif inTimeStr:
86 timestr += c
87 elif inEntry:
88 entry += c
89
90 return entries
91
92 @staticmethod
93 def load(path):
94 """Load a PIREP from the given path.
95
96 Returns the PIREP object, or None on error."""
97 try:
98 with open(path, "rb") as f:
99 pirep = pickle.load(f)
100 if "numCrew" not in dir(pirep):
101 pirep.numCrew = pirep.bookedFlight.numCrew
102 if "numPassengers" not in dir(pirep):
103 pirep.numPassengers = pirep.bookedFlight.numPassengers
104 if "bagWeight" not in dir(pirep):
105 pirep.bagWeight = pirep.bookedFlight.bagWeight
106 if "mailWeight" not in dir(pirep):
107 pirep.mailWeight = pirep.bookedFlight.mailWeight
108 return pirep
109 except Exception, e:
110 print "Failed loading PIREP from %s: %s" % (path,
111 utf2unicode(str(e)))
112 return None
113
114 def __init__(self, flight):
115 """Initialize the PIREP from the given flight."""
116 if flight is None:
117 return
118
119 self.bookedFlight = flight.bookedFlight
120
121 self.numCrew = flight.numCrew
122 self.numPassengers = flight.numPassengers
123 self.bagWeight = flight.bagWeight
124 self.cargoWeight = flight.cargoWeight
125 self.mailWeight = flight.mailWeight
126
127 self.filedCruiseAltitude = flight.filedCruiseAltitude
128 self.cruiseAltitude = flight.cruiseAltitude
129 self.route = flight.route
130
131 self.departureMETAR = flight.departureMETAR.upper()
132 self.arrivalMETAR = flight.arrivalMETAR.upper()
133
134 self.departureRunway = flight.departureRunway.upper()
135 self.sid = flight.sid.upper()
136
137 self.star = flight.star
138 self.transition = flight.transition
139 self.approachType = flight.approachType.upper()
140 self.arrivalRunway = flight.arrivalRunway.upper()
141
142 self.flightType = flight.flightType
143 self.online = flight.online
144
145 self.comments = flight.comments
146 self.flightDefects = flight.flightDefects
147 self.delayCodes = flight.delayCodes
148
149 self.blockTimeStart = flight.blockTimeStart
150 self.flightTimeStart = flight.flightTimeStart
151 self.flightTimeEnd = flight.flightTimeEnd
152 self.blockTimeEnd = flight.blockTimeEnd
153 self.flownDistance = flight.flownDistance
154 self.fuelUsed = flight.startFuel - flight.endFuel
155
156 logger = flight.logger
157 self.rating = logger.getRating()
158 self.logLines = logger.lines
159 self.faultLineIndexes = logger.faultLineIndexes
160
161 def setupFromPIREPData(self, pirepData, bookedFlight):
162
163 self.bookedFlight = bookedFlight
164
165 self.numCrew = int(pirepData["numCrew"])
166 self.numPassengers = int(pirepData["numPassengers"])
167 self.bagWeight = int(pirepData["bagWeight"])
168 self.cargoWeight = int(pirepData["cargoWeight"])
169 self.mailWeight = int(pirepData["mailWeight"])
170
171 self.filedCruiseAltitude = int(pirepData["filedCruiseLevel"][2:])*100
172 cruiseLevel = pirepData["cruiseLevel"].strip()
173 if cruiseLevel:
174 if cruiseLevel.startswith("FL"):
175 cruiseLevel = cruiseLevel[2:]
176 if cruiseLevel:
177 self.cruiseAltitude = int(cruiseLevel[2:])*100
178 else:
179 self.cruiseAltitude = self.filedCruiseAltitude
180 self.route = pirepData["route"]
181
182 self.departureMETAR = pirepData["departureMETAR"]
183 self.arrivalMETAR = pirepData["arrivalMETAR"]
184
185 self.departureRunway = pirepData["departureRunway"]
186 self.sid = pirepData["sid"]
187
188 star = pirepData["star"].split(",")
189 self.star = star[0]
190 self.star.strip()
191
192 if len(star)>1:
193 self.transition = star[1]
194 self.transition.strip()
195 else:
196 self.transition = ""
197 self.approachType = pirepData["approachType"]
198 self.arrivalRunway = pirepData["arrivalRunway"]
199
200 self.flightType = PIREP.decodeFlightTypeText(pirepData["flightType"])
201 self.online = int(pirepData["online"])!=0
202
203 self.comments = pirepData["comments"]
204 self.flightDefects = pirepData["flightDefects"]
205 self.delayCodes = pirepData["timeComment"]
206 if self.delayCodes=="UTC":
207 self.delayCodes = []
208 else:
209 self.delayCodes = self.delayCodes.split(", ")
210
211 flightDate = pirepData["flightDate"] + " "
212
213 self.blockTimeStart = \
214 PIREP.parseTimestampFromRPC(flightDate + pirepData["blockTimeStart"])
215 self.flightTimeStart = \
216 PIREP.parseTimestampFromRPC(flightDate + pirepData["flightTimeStart"])
217 self.flightTimeEnd = \
218 PIREP.parseTimestampFromRPC(flightDate + pirepData["flightTimeEnd"])
219 self.blockTimeEnd = \
220 PIREP.parseTimestampFromRPC(flightDate + pirepData["blockTimeEnd"])
221 self.flownDistance = float(pirepData["flownDistance"])
222 self.fuelUsed = float(pirepData["fuelUsed"])
223
224 # logger = flight.logger
225 self.rating = float(pirepData["rating"])
226
227 log = pirepData["log"]
228
229 self.logLines = PIREP.parseLogFromRPC(log)[1:]
230 if self.logLines and \
231 (self.logLines[0][0]=="LOGGER NG LOG" or
232 self.logLines[0][0]=="MAVA LOGGER X"):
233 self.logLines = self.logLines[1:]
234 numLogLines = len(self.logLines)
235
236 lastFaultLineIndex = 0
237 self.faultLineIndexes = []
238 for ratingText in pirepData["ratingText"].splitlines()[:-1]:
239 faultLines = PIREP.parseLogFromRPC(ratingText)
240 for (timeStr, entry) in faultLines:
241 for i in range(lastFaultLineIndex, numLogLines-1):
242 if timeStr>=self.logLines[i][0] and \
243 timeStr<self.logLines[i+1][0]:
244 self.logLines = self.logLines[:i+1] + \
245 [(timeStr, entry)] + self.logLines[i+1:]
246 self.faultLineIndexes.append(i+1)
247 lastFaultLineIndex = i+1
248 numLogLines += 1
249 break
250
251 @property
252 def flightDateText(self):
253 """Get the text version of the booked flight's departure time."""
254 return self.bookedFlight.departureTime.strftime("%Y-%m-%d")
255
256 @property
257 def flightTypeText(self):
258 """Get the text representation of the flight type."""
259 return PIREP._flightTypes[self.flightType]
260
261 @property
262 def blockTimeStartText(self):
263 """Get the beginning of the block time in string format."""
264 return PIREP.formatTimestampForRPC(self.blockTimeStart)
265
266 @property
267 def flightTimeStartText(self):
268 """Get the beginning of the flight time in string format."""
269 return PIREP.formatTimestampForRPC(self.flightTimeStart)
270
271 @property
272 def flightTimeEndText(self):
273 """Get the end of the flight time in string format."""
274 return PIREP.formatTimestampForRPC(self.flightTimeEnd)
275
276 @property
277 def blockTimeEndText(self):
278 """Get the end of the block time in string format."""
279 return PIREP.formatTimestampForRPC(self.blockTimeEnd)
280
281 def getACARSText(self):
282 """Get the ACARS text.
283
284 This is a specially formatted version of the log without the faults."""
285 text = "[MAVA LOGGER X LOG]-[%s]" % (const.VERSION,)
286 for index in range(0, len(self.logLines)):
287 if index not in self.faultLineIndexes:
288 (timeStr, line) = self.logLines[index]
289 if timeStr is not None:
290 text += PIREP._formatLine(timeStr, line)
291 return text
292
293 def getRatingText(self):
294 """Get the rating text.
295
296 This is a specially formatted version of the lines containing the
297 faults."""
298 text = ""
299 for index in self.faultLineIndexes:
300 (timeStr, line) = self.logLines[index]
301 if timeStr is not None:
302 text += PIREP._formatLine(timeStr, line)
303 text += "\n"
304
305 text += "\n[Flight Rating: %.1f]" % (max(0.0, self.rating),)
306
307 return text
308
309 def getTimeComment(self):
310 """Get the time comment.
311
312 This is basically a collection of the delay codes, if any."""
313 if not self.delayCodes:
314 return "UTC"
315 else:
316 s = ""
317 for code in self.delayCodes:
318 if s: s += ", "
319 s += code
320 return s
321
322 def getSTAR(self):
323 """Get the STAR and/or the transition."""
324 star = self.star if self.star is not None else ""
325 if self.transition is not None:
326 if star: star += ", "
327 star += self.transition
328 return star.upper()
329
330 def save(self, path):
331 """Save the PIREP to the given file.
332
333 Returns whether the saving has succeeded."""
334 try:
335 with open(path, "wb") as f:
336 pickle.dump(self, f)
337 return None
338 except Exception, e:
339 error = utf2unicode(str(e))
340 print u"Failed saving PIREP to %s: %s" % (path, error)
341 return error
342
343 def _serialize(self):
344 """Serialize the PIREP for JSON-RPC."""
345 attrs = {}
346 attrs["log"] = self.getACARSText()
347 attrs["flightDate"] = self.flightDateText
348 attrs["callsign"] = self.bookedFlight.callsign
349 attrs["departureICAO"] = self.bookedFlight.departureICAO
350 attrs["arrivalICAO"] = self.bookedFlight.arrivalICAO
351 attrs["numPassengers"] = self.numPassengers
352 attrs["numCrew"] = self.numCrew
353 attrs["cargoWeight"] = self.cargoWeight
354 attrs["bagWeight"] = self.bagWeight
355 attrs["mailWeight"] = self.mailWeight
356 attrs["flightType"] = self.flightTypeText
357 attrs["online"] = 1 if self.online else 0
358 attrs["blockTimeStart"] = self.blockTimeStartText
359 attrs["blockTimeEnd"] = self.blockTimeEndText
360 attrs["flightTimeStart"] = self.flightTimeStartText
361 attrs["flightTimeEnd"] = self.flightTimeEndText
362 attrs["timeComment"] = self.getTimeComment()
363 attrs["fuelUsed"] = self.fuelUsed
364 attrs["departureRunway"] = self.departureRunway
365 attrs["arrivalRunway"] = self.arrivalRunway
366 attrs["departureMETAR"] = self.departureMETAR
367 attrs["arrivalMETAR"] = self.arrivalMETAR
368 attrs["filedCruiseLevel"] = self.filedCruiseAltitude / 100.0
369 attrs["cruiseLevel"] = self.cruiseAltitude / 100.0
370 attrs["sid"] = self.sid
371 attrs["route"] = self.route
372 attrs["star"] = self.star
373 attrs["approachType"] = self.approachType
374 attrs["comments"] = self.comments
375 attrs["flightDefects"] = self.flightDefects
376 attrs["ratingText"] = self.getRatingText()
377 attrs["rating"] = max(0.0, self.rating)
378 attrs["flownDistance"] = "%.2f" % (self.flownDistance,)
379 # FIXME: it should be stored in the PIREP when it is sent later
380 attrs["performDate"] = datetime.date.today().strftime("%Y-%m-%d")
381
382 return ([], attrs)
Note: See TracBrowser for help on using the repository browser.