Changeset 51:f0f99ac21935


Ignore:
Timestamp:
03/30/12 15:26:43 (12 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
hg-Phase:
(<MercurialRepository 1 'hg:/home/ivaradi/mlx/hg' '/'>, 'public')
Message:

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

Location:
src/mlx
Files:
1 added
5 edited

Legend:

Unmodified
Added
Removed
  • src/mlx/const.py

    r36 r51  
    115115#-------------------------------------------------------------------------------
    116116
     117# Plane status: unknown
     118PLANE_UNKNOWN = 0
     119
     120# Plane status: at home, i.e. LHBP
     121PLANE_HOME = 1
     122
     123# Plane status: away
     124PLANE_AWAY = 2
     125
     126# Plane status: parking
     127PLANE_PARKING = 3
     128
     129#-------------------------------------------------------------------------------
     130
     131# The available gates at LHBP
     132lhbpGateNumbers = []
     133
     134for i in range(1, 7):
     135    lhbpGateNumbers.append(str(i))
     136
     137for i in range(10, 19):
     138    lhbpGateNumbers.append(str(i))
     139
     140for i in range(24, 28):
     141    lhbpGateNumbers.append(str(i))
     142
     143for i in range(31, 39):
     144    lhbpGateNumbers.append(str(i))
     145
     146for i in range(42, 47):
     147    lhbpGateNumbers.append(str(i))
     148
     149for i in range(60, 84):
     150    if i!=70 and i!=80:
     151        lhbpGateNumbers.append(str(i))
     152
     153#-------------------------------------------------------------------------------
     154
    117155_stageStrings = { STAGE_BOARDING : "boarding",
    118156                  STAGE_PUSHANDTAXI : "pushback and taxi",
  • src/mlx/fsuipc.py

    r27 r51  
    1717    import pyuipc
    1818else:
    19     import pyuipc_emu as pyuipc
     19    import pyuipc_sim as pyuipc
    2020
    2121#------------------------------------------------------------------------------
  • src/mlx/gui/flight.py

    r50 r51  
    22
    33from mlx.gui.common import *
     4
     5import mlx.const as const
     6import mlx.fs as fs
     7from mlx.logger import Logger
     8from mlx.flight import Flight
     9from mlx.acft import Aircraft
    410
    511#-----------------------------------------------------------------------------
     
    6268        self._mainAlignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
    6369                                            xscale = 1.0, yscale = 1.0)
    64         table.attach(self._mainAlignment, 0, 1, 1, 3)                                   
     70        table.attach(self._mainAlignment, 0, 1, 1, 3)
    6571                                           
    6672        buttonAlignment =  gtk.Alignment(xalign = 1.0, xscale=0.0, yscale = 0.0)
     
    182188        """Called when the login button was clicked."""
    183189        self._wizard.gui.beginBusy("Logging in...")
    184         self._wizard.gui.webHandler.login(self._pilotID.get_text(),
    185                                           self._password.get_text(),
    186                                           self._loginResultCallback)
     190        self._wizard.gui.webHandler.login(self._loginResultCallback,
     191                                          self._pilotID.get_text(),
     192                                          self._password.get_text())
    187193
    188194    def _loginResultCallback(self, returned, result):
     
    275281        self._button.set_use_stock(True)
    276282        self._button.set_sensitive(False)
     283        self._button.connect("clicked", self._forwardClicked)
    277284
    278285        self._activated = False
     
    292299        self._button.set_sensitive(selection.count_selected_rows()==1)
    293300
     301    def _forwardClicked(self, button):
     302        """Called when the forward button was clicked."""
     303        selection = self._flightList.get_selection()
     304        (listStore, iter) = selection.get_selected()
     305        path = listStore.get_path(iter)
     306        [index] = path.get_indices() if pygobject else path
     307
     308        flight = self._wizard.loginResult.flights[index]
     309        self._wizard._bookedFlight = flight
     310
     311        self._updateDepartureGate()
     312       
     313    def _updateDepartureGate(self):
     314        """Update the departure gate for the booked flight."""
     315        flight = self._wizard._bookedFlight
     316        if flight.departureICAO=="LHBP":
     317            self._wizard._getFleet(self._fleetRetrieved)
     318        else:
     319            self._wizard.jumpPage(2)
     320
     321    def _fleetRetrieved(self, fleet):
     322        """Called when the fleet has been retrieved."""
     323        if fleet is None:
     324            self._wizard.jumpPage(2)
     325        else:
     326            plane = fleet[self._wizard._bookedFlight.tailNumber]
     327            if plane is None:
     328                self._wizard.jumpPage(2)
     329           
     330            if plane.gateNumber is not None and \
     331               not fleet.isGateConflicting(plane):
     332                self._wizard._departureGate = plane.gateNumber
     333                self._wizard.jumpPage(2)
     334            else:
     335                self._wizard.nextPage()
     336       
     337#-----------------------------------------------------------------------------
     338
     339class GateSelectionPage(Page):
     340    """Page to select a free gate at LHBP.
     341
     342    This page should be displayed only if we have fleet information!."""
     343    def __init__(self, wizard):
     344        """Construct the gate selection page."""
     345        help = "The airplane's gate position is invalid.\n\n" \
     346               "Select the gate from which you\n" \
     347               "would like to begin the flight."
     348        super(GateSelectionPage, self).__init__(wizard,
     349                                                "LHBP gate selection",
     350                                                help)
     351
     352        self._listStore = gtk.ListStore(str)
     353        self._gateList = gtk.TreeView(self._listStore)
     354        column = gtk.TreeViewColumn(None, gtk.CellRendererText(),
     355                                    text = 0)
     356        column.set_expand(True)
     357        self._gateList.append_column(column)
     358        self._gateList.set_headers_visible(False)
     359
     360        gateSelection = self._gateList.get_selection()
     361        gateSelection.connect("changed", self._selectionChanged)
     362
     363        scrolledWindow = gtk.ScrolledWindow()
     364        scrolledWindow.add(self._gateList)
     365        scrolledWindow.set_size_request(50, -1)
     366        scrolledWindow.set_policy(gtk.PolicyType.AUTOMATIC if pygobject
     367                                  else gtk.POLICY_AUTOMATIC,
     368                                  gtk.PolicyType.ALWAYS if pygobject
     369                                  else gtk.POLICY_ALWAYS)
     370
     371        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.0, xscale = 0.0, yscale = 1.0)
     372        alignment.add(scrolledWindow)
     373
     374        self.setMainWidget(alignment)       
     375
     376        self._button = self.addButton(gtk.STOCK_GO_FORWARD, default = True)
     377        self._button.set_use_stock(True)
     378        self._button.set_sensitive(False)
     379        self._button.connect("clicked", self._forwardClicked)
     380
     381    def activate(self):
     382        """Fill the gate list."""
     383        self._listStore.clear()
     384        occupiedGateNumbers = self._wizard._fleet.getOccupiedGateNumbers()
     385        for gateNumber in const.lhbpGateNumbers:
     386            if gateNumber not in occupiedGateNumbers:
     387                self._listStore.append([gateNumber])
     388
     389    def _selectionChanged(self, selection):
     390        """Called when the selection is changed."""
     391        self._button.set_sensitive(selection.count_selected_rows()==1)
     392
     393    def _forwardClicked(self, button):
     394        """Called when the forward button is clicked."""
     395        selection = self._gateList.get_selection()
     396        (listStore, iter) = selection.get_selected()
     397        (gateNumber,) = listStore.get(iter, 0)
     398
     399        self._wizard._departureGate = gateNumber
     400
     401        self._wizard._updatePlane(self._planeUpdated,
     402                                  self._wizard._bookedFlight.tailNumber,
     403                                  const.PLANE_HOME,
     404                                  gateNumber)
     405
     406    def _planeUpdated(self, success):
     407        """Callback for the plane updating call."""
     408        if success is None or success:
     409            self._wizard.nextPage()
     410        else:
     411            dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
     412                                       buttons = BUTTONSTYPE_OK,
     413                                       message_format = "Gate conflict detected again")
     414            dialog.format_secondary_markup("Try to select a different gate.")
     415            dialog.run()
     416            dialog.hide()
     417
     418            self._wizard._getFleet(self._fleetRetrieved)
     419
     420    def _fleetRetrieved(self, fleet):
     421        """Called when the fleet has been retrieved."""
     422        if fleet is None:
     423            self._wizard.nextPage()
     424        else:
     425            self.activate()
     426           
     427#-----------------------------------------------------------------------------
     428
     429class ConnectPage(Page):
     430    """Page which displays the departure airport and gate (if at LHBP)."""
     431    def __init__(self, wizard):
     432        """Construct the connect page."""
     433        help = "The flight begins at the airport given below.\n" \
     434               "Park your aircraft there, at the gate below, if given.\n\n" \
     435               "Then press the Connect button to connect to the simulator."
     436        super(ConnectPage, self).__init__(wizard,
     437                                          "Connect to the simulator",
     438                                          help)
     439       
     440        alignment = gtk.Alignment(xalign = 0.5, yalign = 0.5,
     441                                  xscale = 0.0, yscale = 0.0)
     442
     443        table = gtk.Table(2, 2)
     444        table.set_row_spacings(4)
     445        table.set_col_spacings(16)
     446        table.set_homogeneous(True)
     447        alignment.add(table)
     448        self.setMainWidget(alignment)
     449
     450        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     451        label = gtk.Label("ICAO code:")
     452        labelAlignment.add(label)
     453        table.attach(labelAlignment, 0, 1, 0, 1)
     454
     455        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     456        self._departureICAO = gtk.Label()
     457        self._departureICAO.set_width_chars(5)
     458        self._departureICAO.set_alignment(0.0, 0.5)
     459        labelAlignment.add(self._departureICAO)
     460        table.attach(labelAlignment, 1, 2, 0, 1)
     461
     462        labelAlignment = gtk.Alignment(xalign=1.0, xscale=0.0)
     463        label = gtk.Label("Gate:")
     464        label.set_use_underline(True)
     465        labelAlignment.add(label)
     466        table.attach(labelAlignment, 0, 1, 1, 2)
     467
     468        labelAlignment = gtk.Alignment(xalign=0.0, xscale=0.0)
     469        self._departureGate = gtk.Label()
     470        self._departureGate.set_width_chars(5)
     471        self._departureGate.set_alignment(0.0, 0.5)
     472        labelAlignment.add(self._departureGate)
     473        table.attach(labelAlignment, 1, 2, 1, 2)
     474
     475
     476        self._button = self.addButton("_Connect", default = True)
     477        self._button.set_use_underline(True)
     478        self._button.connect("clicked", self._connectClicked)
     479
     480    def activate(self):
     481        """Setup the deprature information."""
     482        icao = self._wizard._bookedFlight.departureICAO
     483        self._departureICAO.set_markup("<b>" + icao + "</b>")
     484        gate = self._wizard._departureGate
     485        if gate!="-":
     486            gate = "<b>" + gate + "</b>"
     487        self._departureGate.set_markup(gate)
     488
     489    def _connectClicked(self, button):
     490        """Called when the Connect button is pressed."""
     491        self._wizard._connectSimulator()
     492
    294493#-----------------------------------------------------------------------------
    295494
     
    307506        self._pages.append(LoginPage(self))
    308507        self._pages.append(FlightSelectionPage(self))
    309 
     508        self._pages.append(GateSelectionPage(self))
     509        self._pages.append(ConnectPage(self))
     510
     511        maxWidth = 0
     512        maxHeight = 0
     513        for page in self._pages:
     514            page.show_all()
     515            pageSizeRequest = page.size_request()
     516            width = pageSizeRequest.width if pygobject else pageSizeRequest[0]
     517            height = pageSizeRequest.height if pygobject else pageSizeRequest[1]
     518            maxWidth = max(maxWidth, width)
     519            maxHeight = max(maxHeight, height)
     520        maxWidth += 16
     521        maxHeight += 32
     522        self.set_size_request(maxWidth, maxHeight)
     523
     524        self._fleet = None
     525        self._fleetCallback = None
     526        self._updatePlaneCallback = None
     527       
    310528        self._loginResult = None
    311 
     529        self._bookedFlight = None
     530        self._departureGate = "-"
     531
     532        self._logger = Logger(output = gui)
     533        self._flight = None
     534        self._simulator = None
     535       
    312536        self.setCurrentPage(0)
    313 
     537       
    314538    @property
    315539    def loginResult(self):
     
    331555    def nextPage(self):
    332556        """Go to the next page."""
    333         self.setCurrentPage(self._currentPage + 1)
     557        self.jumpPage(1)
     558
     559    def jumpPage(self, count):
     560        """Go to the page which is 'count' pages after the current one."""
     561        self.setCurrentPage(self._currentPage + count)
    334562        self.grabDefault()
    335563
     
    337565        """Make the default button of the current page the default."""
    338566        self._pages[self._currentPage].grabDefault()
     567
     568    def _getFleet(self, callback, force = False):
     569        """Get the fleet, if needed.
     570
     571        callback is function that will be called, when the feet is retrieved,
     572        or the retrieval fails. It should have a single argument that will
     573        receive the fleet object on success, None otherwise.
     574        """
     575        if self._fleet is not None and not force:
     576            callback(self._fleet)
     577
     578        self.gui.beginBusy("Retrieving fleet...")
     579        self._fleetCallback = callback
     580        self.gui.webHandler.getFleet(self._fleetResultCallback)
     581
     582    def _fleetResultCallback(self, returned, result):
     583        """Called when the fleet has been queried."""
     584        gobject.idle_add(self._handleFleetResult, returned, result)
     585
     586    def _handleFleetResult(self, returned, result):
     587        """Handle the fleet result."""
     588        self.gui.endBusy()
     589        if returned:
     590            self._fleet = result.fleet
     591        else:
     592            self._fleet = None
     593
     594            dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
     595                                       buttons = BUTTONSTYPE_OK,
     596                                       message_format =
     597                                       "Failed to retrieve the information on "
     598                                       "the fleet.")
     599            dialog.run()
     600            dialog.hide()
     601
     602        self._fleetCallback(self._fleet)
     603
     604    def _updatePlane(self, callback, tailNumber, status, gateNumber = None):
     605        """Update the given plane's gate information."""
     606        self.gui.beginBusy("Updating plane status...")
     607        self._updatePlaneCallback = callback
     608        self.gui.webHandler.updatePlane(self._updatePlaneResultCallback,
     609                                        tailNumber, status, gateNumber)
     610
     611    def _updatePlaneResultCallback(self, returned, result):
     612        """Callback for the plane updating operation."""
     613        gobject.idle_add(self._handleUpdatePlaneResult, returned, result)
     614
     615    def _handleUpdatePlaneResult(self, returned, result):
     616        """Handle the result of a plane update operation."""
     617        self.gui.endBusy()
     618        if returned:
     619            success = result.success
     620        else:
     621            success = None
     622
     623            dialog = gtk.MessageDialog(type = MESSAGETYPE_ERROR,
     624                                       buttons = BUTTONSTYPE_OK,
     625                                       message_format =
     626                                       "Failed to update the statuis of "
     627                                       "the airplane.")
     628            dialog.run()
     629            dialog.hide()
     630
     631        self._updatePlaneCallback(success)
     632
     633    def _connectSimulator(self):
     634        """Connect to the simulator."""
     635        self._logger.reset()
     636        self._flight = Flight(self.gui._logger, self.gui)
     637
     638        self._flight.aircraftType = self._bookedFlight.aircraftType
     639        aircraft = self._flight.aircraft = Aircraft.create(self._flight)
     640        self._flight.aircraft._checkers.append(self.gui)
     641       
     642        self._flight.cruiseAltitude = -1       
     643        self._flight.zfw = -1
     644
     645        if self._simulator is None:
     646            self._simulator = fs.createSimulator(const.SIM_MSFS9, self.gui)
     647
     648        self._flight.simulator = self._simulator
     649        self._simulator.connect(aircraft)
     650        #self._simulator.startMonitoring()
    339651   
    340652#-----------------------------------------------------------------------------
  • src/mlx/gui/gui.py

    r50 r51  
    251251    def beginBusy(self, message):
    252252        """Begin a period of background processing."""
    253         print dir(self._mainWindow)
    254253        self._mainWindow.get_window().set_cursor(self._busyCursor)
    255254        self._statusbar.updateBusyState(message)
     
    285284            self._writeLog(text)
    286285            self._stdioAfterNewLine = False
    287            
     286
    288287    def _connectToggled(self, button):
    289288        """Callback for the connection button."""
  • src/mlx/web.py

    r48 r51  
    77import threading
    88import sys
     9import urllib
    910import urllib2
    1011import hashlib
     
    102103#------------------------------------------------------------------------------
    103104
     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
    104190class Result(object):
    105191    """A result object.
     
    161247    iso88592decoder = codecs.getdecoder("iso-8859-2")
    162248   
    163     def __init__(self, pilotID, password, callback):
     249    def __init__(self, callback, pilotID, password):
    164250        """Construct the login request with the given pilot ID and
    165251        password."""
     
    205291                                    flight2.departureTime))
    206292
     293        f.close()
     294
    207295        return result
    208296       
     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           
    209350#------------------------------------------------------------------------------
    210351
     
    223364        self.daemon = True
    224365
    225     def login(self, pilotID, password, callback):
     366    def login(self, callback, pilotID, password):
    226367        """Enqueue a login request."""
    227         self._addRequest(Login(pilotID, password, callback))
    228 
     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       
    229378    def run(self):
    230379        """Process the requests."""
     
    255404    handler.start()
    256405
    257     handler.login("P096", "V5fwj", callback)
     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")
    258410    time.sleep(3)   
    259 
    260 #------------------------------------------------------------------------------
     411    handler.getFleet(callback)
     412    time.sleep(3)   
     413
     414#------------------------------------------------------------------------------
Note: See TracChangeset for help on using the changeset viewer.