def bufstate(self): """False if no buffered data is available. True if buffered data is ready to use. Float value 0..1 indicates read progress. To wait, use "while bufstate is not True". """ if self._databuffer['error']: self._stopthread = True raise self._databuffer['error'] # Raising NoneType pylint: disable=E0702 if self._databuffer['lastupdate'] is None: self._databuffer['lastupdate'] = time() if self._databuffer['index'] == self._databuffer['lastindex']: if time() - float( self._databuffer['lastupdate']) > self.timeout / 1000.: self._stopthread = True raise GCSError( gcserror.COM_TIMEOUT__7, '@ GCSMessages.bufstate after %.1f s' % (self.timeout / 1000.)) else: self._databuffer['lastupdate'] = time() self._databuffer['lastindex'] = self._databuffer['index'] if not self._databuffer['size']: return False if self._databuffer['size'] is True: self._databuffer['lastupdate'] = None return True return float(self._databuffer['index']) / float( self._databuffer['size'])
def _check_no_eol(answer): """Check that 'answer' does not contain a LF without a preceeding SPACE except at the end. @param answer : Answer to verify as string. """ for i, char in enumerate(answer[:-1]): if char == '\n' or char == '\r': if i > 0 and answer[i - 1] != ' ': msg = '@ GCSMessages._check_no_eol: LF/CR at %r' % answer[ max(0, i - 10):min(i + 10, len(answer))] raise GCSError(gcserror.E_1004_PI_UNEXPECTED_RESPONSE, msg)
def _endofdata(self, line): """Verify 'line' and return True if 'line' is last line of device answer. @param line : One answer line of device with trailing line feed character. @return : True if 'line' is last line of device answer. """ if eol(line) and self._databuffer['size'] and self._databuffer[ 'index'] < self._databuffer['size']: msg = '%s expected, %d received' % (self._databuffer['size'], self._databuffer['index']) exc = GCSError(gcserror.E_1088_PI_TOO_FEW_GCS_DATA, msg) self._databuffer['error'] = exc error('GCSMessages: GCSError: %s', exc) if self._databuffer['size'] and self._databuffer[ 'index'] > self._databuffer['size']: msg = '%s expected, %d received' % (self._databuffer['size'], self._databuffer['index']) exc = GCSError(gcserror.E_1089_PI_TOO_MANY_GCS_DATA, msg) self._databuffer['error'] = exc error('GCSMessages: GCSError: %s', exc) return eol(line)
def _checkerror(self, senderr=True, doraise=True): """Query error from device and raise GCSError exception. @param senderr : If True send "ERR?\n" to the device. @param doraise : If True an error is raised, else the GCS error number is returned. @return : If doraise is False the GCS exception if an error occured else None. """ if not self.errcheck: return 0 if senderr: self._send('ERR?\n') answer = self._read(stopon=None) exc = None try: err = int(answer) except ValueError: exc = GCSError(gcserror.E_1004_PI_UNEXPECTED_RESPONSE, 'invalid answer on "ERR?": %r' % answer) else: if err: exc = GCSError(err) if exc and doraise: raise exc # Raising NoneType while only classes or instances are allowed pylint: disable=E0702 return exc
def _read(self, stopon): """Read answer from device until this ends with linefeed with no preceeding space. @param stopon: Addditional uppercase string that stops reading, too. @return : Received data as string. """ rcvbuf = u'' timeout = time() + self.timeout / 1000. while not eol(rcvbuf): received = self._interface.read() if received: rcvbuf += received.decode('cp1252', errors='replace') timeout = time() + self.timeout / 1000. if time() > timeout: raise GCSError(gcserror.E_7_COM_TIMEOUT, '@ GCSMessages._read') if stopon and stopon in rcvbuf.upper(): break self._check_no_eol(rcvbuf) return rcvbuf
def _convertfloats(self, line): """Convert items in 'line' to float and append them to 'self._databuffer'. @param line : One line in qDRR answer with data values as string. """ numcolumns = len(self._databuffer['data']) msg = 'cannot convert to float: %r' % line try: values = [float(x) for x in line.split()] if numcolumns != len(values): msg = 'expected %d, got %d columns: %r' % (numcolumns, len(values), line) raise ValueError() except ValueError: exc = GCSError(gcserror.E_1004_PI_UNEXPECTED_RESPONSE, msg) self._databuffer['error'] = exc error('GCSMessages: GCSError: %s', exc) else: for i in range(numcolumns): self._databuffer['data'][i].append(values[i]) self._databuffer['index'] += 1
def read(self, tosend, gcsdata=0): """Send 'tosend' to device, read answer and check for error. @param tosend : String to send to device. @param gcsdata : Number of lines, if != 0 then GCS data will be read in background task. @return : Device answer as string. """ if gcsdata is not None: gcsdata = None if gcsdata < 0 else gcsdata stopon = None if gcsdata != 0: stopon = '# END_HEADER' self._databuffer['data'] = [] self._databuffer['index'] = 0 self._databuffer['error'] = None with self._lock: self._send(tosend) answer = self._read(stopon) if gcsdata != 0: splitpos = answer.upper().find(stopon) if splitpos < 0: self._send('ERR?\n') err = int(self._read(stopon=None).strip()) err = err or gcserror.E_1004_PI_UNEXPECTED_RESPONSE raise GCSError( err, '@ GCSMessages.read, no %r in %r' % (stopon, answer)) stopon += ' \n' splitpos += len(stopon) strbuf = answer[ splitpos:] # split at "# END HEADER \n", this is the data answer = answer[: splitpos] # split at "# END HEADER \n", this is the header if stopon in answer.upper( ): # "# END HEADER\n" will not start reading GCS data self._databuffer['size'] = gcsdata self._readgcsdata(strbuf) else: self._databuffer['size'] = True else: self._checkerror() return answer
def _fillbuffer(self, answer, stop): """Read answers and save them as float values into the data buffer. An answerline with invalid data (non-number, missing column) will be skipped and error flag is set. @param answer : String of already readout answer. @param stop : Callback function that stops the loop if True. """ with self._lock: while True: lines = answer.splitlines(True) # keep line endings answer = '' for line in lines: if '\n' not in line: answer = line break self._convertfloats(line) if self._endofdata(line): debug( 'GCSMessages: end background task to query GCS data' ) if not self._databuffer['error']: self._databuffer['error'] = self._checkerror( doraise=False) if not self._databuffer['error']: self._databuffer['size'] = True return try: answer += self._read(stopon=' \n') except: # No exception type(s) specified pylint: disable=W0702 exc = GCSError(gcserror.E_1090_PI_GCS_DATA_READ_ERROR, sys.exc_info()[1]) self._databuffer['error'] = exc error('GCSMessages: end background task with GCSError: %s', exc) self._databuffer['size'] = True return if stop(): error( 'GCSMessages: stop background task to query GCS data') return