def __fillbuffer(self, answer): """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. """ numcolumns = len(self.__databuffer['data']) 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 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 if self.__endofdata(line): debug( 'GCSMessages: end background task to query GCS data' ) self.__databuffer['error'] = self.__checkerror( doraise=False) 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
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. """ timeout = time() + self.__timeout / 1000. chunks = [] while True: if time() > timeout: raise GCSError(gcserror.E_7_COM_TIMEOUT) if self.__interface.answersize: timeout = time() + self.__timeout / 1000. received = self.__interface.getanswer( self.__interface.answersize) chunks.append(received) if endofanswer(chunks[-1]): break if stopon and stopon in chunks[-1].upper(): break try: answer = ''.join(chunks) except TypeError: answer = b''.join(chunks) self.__check_no_eol(answer) return answer
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 GetAsyncBuffer(self, firstline=1, lastline=0, numtables=1): """Query all available data points, return list with 'numtables' columns. DEPRECATED: Use GCSMessages.bufdata instead. Buffer is used by qDRR(), qDDL(), qGWD(), qTWS(), qJLT() and qHIT(). @param firstline : Optional, but must be >= 1 and smaller than 'lastline'. @param lastline : Optional, defaults to query all available data points. @param numtables : Arrange data into 'numtables' columns, defaults to "1". @return: List of data points as float with 'numtables' columns. """ debug( 'DEPRECATED -- GcsDll.GetAsyncBuffer(id%d, firstline=%r, lastline=%r, numtables=%r)', self.__id, firstline, lastline, numtables) maxindex = lastline * numtables or self.__asyncbufferindex assert firstline >= 1, 'firstline must be 1 or larger' minindex = (firstline - 1) * numtables assert minindex <= maxindex, 'firstline must not be larger than lastline' cvalues = ctypes.byref(ctypes.c_float) if not getattr(self.__handle, self.__prefix + 'GetAsyncBuffer')( self.__id, cvalues): raise GCSError(self.__error) data = [[] for _ in range(numtables)] for i in range(minindex, maxindex): data[i % numtables].append(float(cvalues[i])) debug('DEPRECATED -- GCSDll.GetAsyncBuffer(id%d): %r', self.__id, data) return data
def OpenTCPIPDaisyChain(self, ipaddress, ipport=50000): """Open a TCPIP daisy chain connection. To get access to a daisy chain device you have to call ConnectDaisyChainDevice(). @param ipaddress: IP address to connect to as string. @param ipport: Port to use as integer, defaults to 50000. @return: Found devices as list of strings. """ debug('GCSDll.OpenTCPIPDaisyChain(ipaddress=%r, ipport=%s)', ipaddress, ipport) cipaddress = ctypes.c_char_p(ipaddress.encode()) cipport = ctypes.c_int(ipport) numdev = ctypes.byref(ctypes.c_int()) bufsize = 10000 bufstr = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) self.__dcid = getattr(self.__handle, self.__prefix + 'OpenTCPIPDaisyChain')(cipaddress, cipport, numdev, bufstr, bufsize) if self.__dcid < 0: raise GCSError(self.__error) devlist = bufstr.value.decode().split('\n')[:-1] devlist = [item.strip() for item in devlist] debug('GCSDll.OpenTCPIPDaisyChain: %r', devlist) self.__ifdescription = 'TCPIP daisy chain at %s:%s' % (ipaddress, ipport) return devlist
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 _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
def answersize(self): """Get the size of an answer of a GCS command as integer.""" size = ctypes.c_int() if not getattr(self.__handle, self.__prefix + 'GcsGetAnswerSize')( self.__id, ctypes.byref(size)): raise GCSError(self.__error) return size.value
def OpenRS232DaisyChain(self, comport, baudrate): """Open an RS-232 daisy chain connection. To get access to a daisy chain device you have to call ConnectDaisyChainDevice(). @param comport: Port to use as integer (1 means "COM1"). @param baudrate: Baudrate to use as integer. @return: Found devices as list of strings. """ debug('GCSDll.OpenRS232DaisyChain(comport=%s, baudrate=%s)', comport, baudrate) ccomport = ctypes.c_int(comport) cbaudrate = ctypes.c_int(baudrate) numdev = ctypes.byref(ctypes.c_int()) bufsize = 10000 bufstr = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) self.__dcid = getattr(self.__handle, self.__prefix + 'OpenRS232DaisyChain')(ccomport, cbaudrate, numdev, bufstr, bufsize) if self.__dcid < 0: raise GCSError(self.__error) devlist = bufstr.value.decode().split('\n')[:-1] devlist = [item.strip() for item in devlist] debug('GCSDll.OpenRS232DaisyChain: %r', devlist) self.__ifdescription = 'RS-232 daisy chain at COM%s, %s Baud' % ( comport, baudrate) return devlist
def send(self, command): """Send a GCS command to the device, do not query error from device. @param command : GCS command as string, with or without trailing line feed character. """ debug('GCSDll.send(id%d): %r', self.__id, command) command = ctypes.c_char_p(command.encode()) if not getattr(self.__handle, self.__prefix + 'GcsCommandset')(self.__id, command): raise GCSError(self.__error)
def RemoveStage(self, axis): """Remove a dataset of a user defined stage from the PI stages database. @param axis: Name of axis whose stage parameters should be removed as string. """ debug('GCSDll.RemoveStage(axis=%r)', axis) axis = ctypes.c_char_p(str(axis).encode()) if not getattr(self.__handle, self.__prefix + 'RemoveStage')(self.__id, axis): raise GCSError(self.__error)
def AddStage(self, axis): """Add a dataset for a user defined stage to the PI stages database. @param axis: Name of axis whose stage parameters should be added as string. """ debug('GCSDll.AddStage(axis=%r)', axis) assert isinstance(axis, basestring), 'argument for AddStage must be string' axis = ctypes.c_char_p(str(axis).encode()) if not getattr(self.__handle, self.__prefix + 'AddStage')(self.__id, axis): raise GCSError(self.__error)
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 InterfaceSetupDlg(self, key=''): """Open dialog to select the interface. @param key: Optional key name as string to store the settings in the Windows registry. """ debug('GCSDll.InterfaceSetupDlg(key=%r)', key) key = ctypes.c_char_p(key.encode()) self.__id = getattr(self.__handle, self.__prefix + 'InterfaceSetupDlg')(key) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'Interface Setup Dialog'
def ConnectTCPIPByDescription(self, description): """Open a TCP/IP connection to the device using the device 'description'. @param description: One of the identification strings listed by EnumerateTCPIPDevices(). """ debug('GCSDll.ConnectTCPIPByDescription(description=%r)', description) cdescription = ctypes.c_char_p(description.encode()) self.__id = getattr(self.__handle, self.__prefix + 'ConnectTCPIPByDescription')(cdescription) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'TCPIP %r' % description
def ConnectUSB(self, serialnum): """Open an USB connection to a device. @param serialnum: Serial number of device or one of the identification strings listed by EnumerateUSB(). """ debug('GCSDll.ConnectUSB(serialnum=%r)', serialnum) cserialnum = ctypes.c_char_p(serialnum.encode()) self.__id = getattr(self.__handle, self.__prefix + 'ConnectUSB')(cserialnum) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'USB %r' % serialnum
def ConnectTCPIP(self, ipaddress, ipport=50000): """Open a TCP/IP connection to the device. @param ipaddress: IP address to connect to as string. @param ipport: Port to use as integer, defaults to 50000. """ debug('GCSDll.ConnectTCPIP(ipaddress=%s, ipport=%s)', ipaddress, ipport) cipaddress = ctypes.c_char_p(ipaddress.encode()) cipport = ctypes.c_int(ipport) self.__id = getattr(self.__handle, self.__prefix + 'ConnectTCPIP')(cipaddress, cipport) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'TCPIP %s:%s' % (ipaddress, ipport)
def ConnectNIgpib(self, board, device): """Open a connection from a NI IEEE 488 board to the device. @param board: GPIB board ID as integer. @param device: The GPIB device ID of the device as integer. """ debug('GCSDll.ConnectNIgpib(board=%s, device=%s)', board, device) cboard = ctypes.c_int(board) cdevice = ctypes.c_int(device) self.__id = getattr(self.__handle, self.__prefix + 'ConnectNIgpib')(cboard, cdevice) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'GPIB board %s, device %s' % (board, device)
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, 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 ConnectPciBoard(self, board): """Open a PCI board connection. @param board : PCI board number as integer. """ debug('GCSDll.ConnectPciBoard(board=%s)', board) cboard = ctypes.c_int(board) if self.__prefix == 'C843_': self.__id = getattr(self.__handle, self.__prefix + 'Connect')(cboard) else: self.__id = getattr(self.__handle, self.__prefix + 'ConnectPciBoard')(cboard) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'PCI board %s' % board
def getconfigs(self): """Get available configurations in PIStages database for the connected controller. @return : Answer as string. """ debug('GCSDll.getconfigs()') bufsize = 20000 configs = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) funcname = self.__prefix + 'GetAvailableControllerConfigurationsFromDatabase' if not getattr(self.__handle, funcname)(self.__id, configs, bufsize + 1): raise GCSError(self.__error) configs = configs.value.decode(encoding='cp1252') debug('GCSDll.getconfigs(id%d): %r', self.__id, configs) return configs
def getanswer(self, bufsize): """Get the answer of a GCS command. @param bufsize : Size in characters of string buffer to store the answer as integer. @return : Answer as string. """ bufstr = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) if not getattr(self.__handle, self.__prefix + 'GcsGetAnswer')(self.__id, bufstr, bufsize + 1): raise GCSError(self.__error) try: answer = bufstr.value.decode(encoding='cp1252') except UnicodeDecodeError: answer = bufstr.value debug('GCSDll.getanswer(id%d): %r', self.__id, answer) return answer
def saveconfig(self, items, config): """Read parameters according to 'config' from controller and write them to the PIStages database. @param items : Items of the controller the configuration is assigned to as string. Consists of the key word (e.g. "axis") and ID (e.g. "4"), examples: "axis 1", "axis 4". @param config: Name of a configuration not yet existing in PIStages database as string. """ debug('GCSDll.saveconfig(items=%r, config=%r)', items, config) items = ctypes.c_char_p(str(items).encode()) config = ctypes.c_char_p(str(config).encode()) bufsize = 20000 warnings = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) funcname = self.__prefix + 'ReadConfigurationFromControllerToDatabase' if not getattr(self.__handle, funcname)(self.__id, items, config, warnings, bufsize + 1): self.__warnmsg = warnings.value.decode(encoding='cp1252') raise GCSError(self.__error)
def EnumerateTCPIPDevices(self, mask=''): """Get identification strings of all TCP connected devices. @param mask: String to filter the results for certain text. @return: Found devices as list of strings. """ debug('GCSDll.EnumerateTCPIPDevices(mask=%r)', mask) mask = ctypes.c_char_p(mask.encode()) bufsize = 100000 bufstr = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) if getattr(self.__handle, self.__prefix + 'EnumerateTCPIPDevices')(bufstr, bufsize, mask) < 0: raise GCSError(self.__error) devlist = bufstr.value.decode().split('\n')[:-1] devlist = [item.strip() for item in devlist] debug('GCSDll.EnumerateTCPIPDevices: %r', devlist) return devlist
def ConnectDaisyChainDevice(self, deviceid, daisychainid=None): """Connect device with 'deviceid' on the daisy chain 'daisychainid'. Daisy chain has to be connected before, see Open<interface>DaisyChain() functions. @param daisychainid : Daisy chain ID as int from the daisy chain master instance or None. @param deviceid : Device ID on the daisy chain as integer. """ debug('GCSDll.ConnectDaisyChainDevice(deviceid=%s, daisychainid=%s)', deviceid, daisychainid) if daisychainid is None: daisychainid = self.__dcid cdeviceid = ctypes.c_int(deviceid) cdaisychainid = ctypes.c_int(daisychainid) self.__id = getattr(self.__handle, self.__prefix + 'ConnectDaisyChainDevice')(cdaisychainid, cdeviceid) if self.__id < 0: raise GCSError(self.__error) if self.__ifdescription: self.__ifdescription += '; ' self.__ifdescription += 'daisy chain %d, device %s' % (daisychainid, deviceid)
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 OpenUSBDaisyChain(self, description): """Open a USB daisy chain connection. To get access to a daisy chain device you have to call ConnectDaisyChainDevice(). @param description: Description of the device returned by EnumerateUSB(). @return: Found devices as list of strings. """ debug('GCSDll.OpenUSBDaisyChain(description=%r)', description) cdescription = ctypes.c_char_p(description.encode()) numdev = ctypes.byref(ctypes.c_int()) bufsize = 10000 bufstr = ctypes.create_string_buffer('\000'.encode(), bufsize + 2) self.__dcid = getattr(self.__handle, self.__prefix + 'OpenUSBDaisyChain')(cdescription, numdev, bufstr, bufsize) if self.__dcid < 0: raise GCSError(self.__error) devlist = bufstr.value.decode().split('\n')[:-1] devlist = [item.strip() for item in devlist] debug('GCSDll.OpenUSBDaisyChain: %r', devlist) self.__ifdescription = 'USB daisy chain at SN %r' % description return devlist
def ConnectRS232(self, comport, baudrate): """Open an RS-232 connection to the device. @param comport: Port to use as integer (1 means "COM1") or name ("dev/ttys0") as string. @param baudrate: Baudrate to use as integer. """ cbaudrate = ctypes.c_int(baudrate) try: comport = int(comport) except ValueError: debug('GCSDll.ConnectRS232ByDevName(devname=%r, baudrate=%s)', comport, baudrate) cdevname = ctypes.c_char_p(comport.encode()) self.__id = getattr(self.__handle, self.__prefix + 'ConnectRS232ByDevName')(cdevname, cbaudrate) else: debug('GCSDll.ConnectRS232(comport=%s, baudrate=%s)', comport, baudrate) ccomport = ctypes.c_int(comport) self.__id = getattr(self.__handle, self.__prefix + 'ConnectRS232')(ccomport, cbaudrate) if self.__id < 0: raise GCSError(self.__error) self.__ifdescription = 'RS-232 port %s, %s Baud' % (comport, baudrate)
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