Ignore:
Timestamp:
02/14/12 19:14:23 (12 years ago)
Author:
István Váradi <ivaradi@…>
Branch:
default
Phase:
public
Message:

Added support for validating the data read

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/fsuipc.py

    r16 r21  
    5858            return None
    5959
     60    NUM_READATTEMPTS = 3
     61
     62    @staticmethod
     63    def _performRead(data, callback, extra, validator):
     64        """Perform a read request.
     65
     66        If there is a validator, that will be called with the return values,
     67        and if the values are wrong, the request is retried at most a certain
     68        number of times.
     69
     70        Return True if the request has succeeded, False if validation has
     71        failed during all attempts. An exception may also be thrown if there is
     72        some lower-level communication problem."""
     73        attemptsLeft = Handler.NUM_READATTEMPTS
     74        while attemptsLeft>0:
     75            values = pyuipc.read(data)
     76            if validator is None or \
     77               Handler._callSafe(lambda: validator(values, extra)):
     78                Handler._callSafe(lambda: callback(values, extra))
     79                return True
     80            else:
     81                attemptsLeft -= 1
     82        return False
     83
    6084    class Request(object):
    6185        """A simple, one-shot request."""
    62         def __init__(self, forWrite, data, callback, extra):
     86        def __init__(self, forWrite, data, callback, extra, validator = None):
    6387            """Construct the request."""
    6488            self._forWrite = forWrite
     
    6690            self._callback = callback
    6791            self._extra = extra
     92            self._validator = validator
    6893           
    6994        def process(self, time):
    70             """Process the request."""           
     95            """Process the request.
     96
     97            Return True if the request has succeeded, False if data validation
     98            has failed for a reading request. An exception may also be thrown
     99            if there is some lower-level communication problem."""           
    71100            if self._forWrite:
    72101                pyuipc.write(self._data)
    73102                Handler._callSafe(lambda: self._callback(True, self._extra))
     103                return True
    74104            else:
    75                 values = pyuipc.read(self._data)
    76                 Handler._callSafe(lambda: self._callback(values, self._extra))
    77 
    78             return True
     105                return Handler._performRead(self._data, self._callback,
     106                                            self._extra, self._validator)
    79107
    80108        def fail(self):
     
    87115    class PeriodicRequest(object):
    88116        """A periodic request."""
    89         def __init__(self, id,  period, data, callback, extra):
     117        def __init__(self, id,  period, data, callback, extra, validator):
    90118            """Construct the periodic request."""
    91119            self._id = id
     
    96124            self._callback = callback
    97125            self._extra = extra
     126            self._validator = validator
    98127           
    99128        @property
     
    110139            """Check if this request should be executed, and if so, do so.
    111140
    112             Return a boolean indicating if the request was executed."""
    113             if time < self._nextFire:
    114                 return False
    115 
     141            time is the time at which the request is being executed. If this
     142            function is called too early, nothing is done, and True is
     143            returned.
     144
     145            Return True if the request has succeeded, False if data validation
     146            has failed. An exception may also be thrown if there is some
     147            lower-level communication problem."""
     148            if time<self._nextFire:
     149                return True
     150           
    116151            if self._preparedData is None:
    117152                self._preparedData = pyuipc.prepare_data(self._data)
    118153                self._data = None
    119                
    120             values = pyuipc.read(self._preparedData)
    121 
    122             Handler._callSafe(lambda: self._callback(values, self._extra))
    123 
    124             while self._nextFire <= time:
    125                 self._nextFire += self._period
     154
     155            isOK = Handler._performRead(self._preparedData, self._callback,
     156                                        self._extra, self._validator)
     157
     158            if isOK:
     159                while self._nextFire <= time:
     160                    self._nextFire += self._period
    126161           
    127             return True
     162            return isOK
    128163
    129164        def fail(self):
     
    144179        self._requestCondition = threading.Condition()
    145180        self._connectionRequested = False
     181        self._connected = False
    146182
    147183        self._requests = []
     
    151187        self.daemon = True
    152188
    153     def requestRead(self, data, callback, extra = None):
     189    def requestRead(self, data, callback, extra = None, validator = None):
    154190        """Request the reading of some data.
    155191
     
    165201        """
    166202        with self._requestCondition:
    167             self._requests.append(Handler.Request(False, data, callback, extra))
     203            self._requests.append(Handler.Request(False, data, callback, extra,
     204                                                  validator))
    168205            self._requestCondition.notify()
    169206
     
    192229            condition.notify()
    193230
    194     def read(self, data):
    195         """Read the given data synchronously.
    196 
    197         If a problem occurs, an exception is thrown."""
    198         with threading.Condition() as condition:
    199             extra = [condition]
    200             self._requestRead(data, self._readWriteCallback, extra)
    201             while len(extra)<2:
    202                 condition.wait()
    203             if extra[1] is None:
    204                 raise fs.SimulatorException("reading failed")
    205             else:
    206                 return extra[1]
    207 
    208     def write(self, data):
    209         """Write the given data synchronously.
    210 
    211         If a problem occurs, an exception is thrown."""
    212         with threading.Condition() as condition:
    213             extra = [condition]
    214             self._requestWrite(data, self._writeCallback, extra)
    215             while len(extra)<2:
    216                 condition.wait()
    217             if extra[1] is None:
    218                 raise fs.SimulatorException("writing failed")
    219 
    220     def requestPeriodicRead(self, period, data, callback, extra = None):
     231    def requestPeriodicRead(self, period, data, callback, extra = None,
     232                            validator = None):
    221233        """Request a periodic read of data.
    222234
     
    228240            id = self._nextPeriodicID
    229241            self._nextPeriodicID += 1
    230             request = Handler.PeriodicRequest(id, period, data, callback, extra)
     242            request = Handler.PeriodicRequest(id, period, data, callback,
     243                                              extra, validator)
    231244            self._periodicRequests.append(request)
    232245            self._requestCondition.notify()
     
    273286           
    274287    def _connect(self):
    275         """Try to connect to the flight simulator via FSUIPC"""
     288        """Try to connect to the flight simulator via FSUIPC
     289
     290        Returns True if the connection has been established, False if it was
     291        not due to no longer requested.
     292        """
    276293        while self._connectionRequested:
    277294            try:
     
    283300                                  self._connectionListener.connected(const.SIM_MSFS9,
    284301                                                                     description))
     302                self._connected = True
    285303                return True
    286304            except Exception, e:
     
    294312        with self._requestCondition:
    295313            while self._connectionRequested:
    296                 if not self._processRequests():
    297                     return
    298                 timeout = None
    299                 if self._periodicRequests:
    300                     self._periodicRequests.sort()
    301                     timeout = self._periodicRequests[0].nextFire - time.time()
    302                 if timeout is None or timeout > 0.0:
    303                     self._requestCondition.wait(timeout)
     314                self._processRequests()
     315                self._waitRequest()
     316
     317    def _waitRequest(self):
     318        """Wait for the time of the next request.
     319
     320        Returns also, if the connection is no longer requested.
     321
     322        Should be called with the request condition lock held."""
     323        while self._connectionRequested:
     324            timeout = None
     325            if self._periodicRequests:
     326                self._periodicRequests.sort()
     327                timeout = self._periodicRequests[0].nextFire - time.time()
     328
     329            if timeout is not None and timeout <= 0.0:
     330                return
     331           
     332            self._requestCondition.wait(timeout)
    304333               
    305334    def _disconnect(self):
    306335        """Disconnect from the flight simulator."""
    307         pyuipc.close()
    308         Handler._callSafe(lambda: self._connectionListener.disconnected())
     336        if self._connected:
     337            pyuipc.close()
     338            Handler._callSafe(lambda: self._connectionListener.disconnected())
     339            self._connected = False
    309340
    310341    def _failRequests(self, request):
     
    323354        """Process the given request.
    324355
    325         If an exception occurs, we try to reconnect.
    326        
    327         Returns what the request's process() function returned or None if
    328         reconnection failed."""       
     356        If an exception occurs or invalid data is read too many times, we try
     357        to reconnect.
     358
     359        This function returns only if the request has succeeded, or if a
     360        connection is no longer requested.
     361
     362        This function is called with the request lock held, but is relased
     363        whole processing the request and reconnecting."""
    329364        self._requestCondition.release()
    330365
     366        needReconnect = False
    331367        try:
    332             return request.process(time)
    333         except Exception as e:
    334             print "fsuipc.Handler._processRequest: FSUIPC connection failed (" + \
    335                 str(e) + ") reconnecting."
    336             self._disconnect()
    337             self._failRequests(request)
    338             if not self._connect(): return None
    339             else: return True
     368            try:
     369                if not request.process(time):
     370                    print "fsuipc.Handler._processRequest: FSUIPC returned invalid data too many times, reconnecting"
     371                    needReconnect = True
     372            except Exception as e:
     373                print "fsuipc.Handler._processRequest: FSUIPC connection failed (" + \
     374                      str(e) + "), reconnecting."
     375                needReconnect = True
     376
     377            if needReconnect:
     378                self._disconnect()
     379                self._failRequests(request)
     380                self._connect()
    340381        finally:
    341382            self._requestCondition.acquire()
     
    348389            self._periodicRequests.sort()
    349390            request = self._periodicRequests[0]
    350             result = self._processRequest(request, time.time())
    351             if result is None: return False
    352             elif not result: break
     391
     392            t = time.time()
     393
     394            if request.nextFire>t:
     395                break
     396           
     397            self._processRequest(request, t)
    353398
    354399        while self._connectionRequested and self._requests:
     
    356401            del self._requests[0]
    357402
    358             if self._processRequest(request, None) is None:
    359                 return False
     403            self._processRequest(request, None)
    360404
    361405        return self._connectionRequested
     
    481525        """Start the default normal periodic request."""
    482526        assert self._normalRequestID is None
    483         self._normalRequestID = self._handler.requestPeriodicRead(1.0,
    484                                                                   Simulator.normalData,
    485                                                                   self._handleNormal)
     527        self._normalRequestID = \
     528             self._handler.requestPeriodicRead(1.0,
     529                                               Simulator.normalData,
     530                                               self._handleNormal,
     531                                               validator = self._validateNormal)
    486532
    487533    def _stopNormal(self):
     
    491537        self._normalRequestID = None
    492538        self._monitoring = False
     539
     540    def _validateNormal(self, data, extra):
     541        """Validate the normal data."""
     542        return data[0]!=0 and data[1]!=0 and len(data[5])>0 and len(data[6])>0
    493543
    494544    def _handleNormal(self, data, extra):
     
    565615        self._normalRequestID = \
    566616            self._handler.requestPeriodicRead(1.0, data,
    567                                               self._handleNormal)
     617                                              self._handleNormal,
     618                                              validator = self._validateNormal)
    568619        self._monitoring = True
    569620
Note: See TracChangeset for help on using the changeset viewer.