Пример #1
0
    def reconnect(self):  # recreate a broken connection
        """reconnect() creates or recreates our main connection.  Useful in __init__ and in complete communications breakdowns.
    If it throws a VXI_11_Transient_Error, the connection exists, but the check_idn() handshake or post_init() failed."""

        self.connected = 0

        if self.core:
            # if this is a reconnect, break old connection the hard way
            self.core.close()
        if self.abort_channel:
            self.abort_channel.close()

        self.core = rpc.TCPClient(self.host,
                                  395183,
                                  1,
                                  portmap_proxy_host=self.portmap_proxy_host,
                                  portmap_proxy_port=self.portmap_proxy_port)

        self._core_packers, self._core_unpackers = self._link_xdr_defs(
            self.core)  # construct xdr data type definitions for the core

        err, self.lid, self.abortPort, self.maxRecvSize = self.command(
            10,
            "create_link",
            "create_link", (0, 0, self.timeout, self.device_sicl_name),
            ignore_connect=1)  # execute create_link

        # at this stage, we always raise exceptions since there isn't any way to
        # bail out or retry reasonably
        if err:
            raise VXI_11_Error(err)

        # never transfer more than 1MB at a shot
        self.maxRecvSize = min(self.maxRecvSize, 1048576)

        if self.abortPort and self.OneWayAbort:
            #self.abort_channel=OneWayAbortClient(self.host, 395184, 1, self.abortPort)
            self.abort_channel = rpc.RawUDPClient(self.host, 395184, 1,
                                                  self.abortPort)
        elif self.abortPort:
            self.abort_channel = RawTCPClient(self.host, 395184, 1,
                                              self.abortPort)
        else:
            self.abort_channel = None

        connection_dict[self.lid] = (self.device_name, weakref.ref(self))

        self.locklevel = 0

        self.connected = 1

        self.check_idn()
        self.post_init()
Пример #2
0
  def reconnect(self):  # recreate a broken connection
    """reconnect() creates or recreates our main connection.  Useful in __init__ and in complete communications breakdowns.
    If it throws a VXI_11_Transient_Error, the connection exists, but the check_idn() handshake or post_init() failed."""

    self.connected = 0

    if self.core:
      # if this is a reconnect, break old connection the hard way
      self.core.close()
    if self.abort_channel:
      self.abort_channel.close()

    self.core = rpc.TCPClient(self.host, 395183, 1,
                              portmap_proxy_host=self.portmap_proxy_host,
                              portmap_proxy_port=self.portmap_proxy_port)

    self._core_packers, self._core_unpackers = self._link_xdr_defs(
        self.core)  # construct xdr data type definitions for the core

    err, self.lid, self.abortPort, self.maxRecvSize = self.command(
        10, "create_link", "create_link",
        (0, 0, self.timeout, self.device_sicl_name),
        ignore_connect=1)  # execute create_link

    # at this stage, we always raise exceptions since there isn't any way to
    # bail out or retry reasonably
    if err:
      raise VXI_11_Error(err)

    # never transfer more than 1MB at a shot
    self.maxRecvSize = min(self.maxRecvSize, 1048576)

    if self.abortPort and self.OneWayAbort:
      #self.abort_channel=OneWayAbortClient(self.host, 395184, 1, self.abortPort)
      self.abort_channel = rpc.RawUDPClient(
          self.host, 395184, 1, self.abortPort)
    elif self.abortPort:
      self.abort_channel = RawTCPClient(self.host, 395184, 1, self.abortPort)
    else:
      self.abort_channel = None

    connection_dict[self.lid] = (self.device_name, weakref.ref(self))

    self.locklevel = 0

    self.connected = 1

    self.check_idn()
    self.post_init()
Пример #3
0
	def reconnect(self): #recreate a broken connection
		"""reconnect() creates or recreates our main connection.  Useful in __init__ and in complete communications breakdowns.
		If it throws a VXI_11_Transient_Error, the connection exists, but the check_idn() handshake or post_init() failed."""
		
		self.connected=0
				
		if self.core:
			self.core.close() #if this is a reconnect, break old connection the hard way
		if self.abortChannel:
			self.abortChannel.close()
			
		self.core=rpc.TCPClient(self.host, 395183, 1, 
				portmap_proxy_host=self.portmap_proxy_host, 
				portmap_proxy_port=self.portmap_proxy_port)
				
		self._core_packers, self._core_unpackers=self._link_xdr_defs(self.core) #construct xdr data type definitions for the core
		
		err, self.lid, self.abortPort, self.maxRecvSize=self.command(
			10, "create_link","create_link", (0, 0, self.timeout, self.device_sicl_name), ignore_connect=1) #execute create_link
		
		if err: #at this stage, we always raise exceptions since there isn't any way to bail out or retry reasonably
			raise VXI_11_Error(err)
		
		self.maxRecvSize=min(self.maxRecvSize, 1048576) #never transfer more than 1MB at a shot
					
		if self.OneWayAbort:
			#self.abort_channel=OneWayAbortClient(self.host, 395184, 1, self.abortPort)
			self.abort_channel=rpc.RawUDPClient(self.host, 395184, 1, self.abortPort)
		else:
			self.abort_channel=RawTCPClient(self.host, 395184, 1, self.abortPort)
			
		connection_dict[self.lid]=(self.device_name, weakref.ref(self))

		self.locklevel=0

		self.connected=1

		# Not doing this next line because we never know what we have for
		# connections until the database tells us it's what we are supposed
		# to have. We simply want to collect the information.
		#self.check_idn()
		
		# Here is where we collect the information from the GPIB instrument
		# (if there is one at this address).
		self.queryIDN()
		
		self.post_init()            
Пример #4
0
class vxi_11_connection:
	"""vxi_11_connection implements handling of devices compliant with vxi11.1-vxi11.3 protocols, with which
	the user should have some familiarity"""
	
	debug_info=0
	debug_error=1
	debug_warning=2
	debug_all=3
	
	debug_level=debug_error
	
	OneWayAbort=0 #by default, this class uses two-way aborts, per official vxi-11 standard
	
	def _list_packer(self, args):
		l=map(None, self.pack_type_list, args) # combine lists
		for packer, data in l:
			packer(data)

	def _list_unpacker(self):
		return [func() for func in self.unpack_type_list]
 
	def _link_xdr_defs(self, channel):
		"self.link_xdr_defs() creates dictionaries of functions for packing and unpacking the various data types"
		p=channel.packer
		u=channel.unpacker

		xdr_packer_defs={
			"write":  (p.pack_int, p.pack_int, p.pack_int, p.pack_int, p.pack_opaque),
			"read":  (p.pack_int, p.pack_int, p.pack_int, p.pack_int, p.pack_int, p.pack_int),
			"create_link": (p.pack_int, p.pack_bool, p.pack_uint, p.pack_string), 
			"generic": (p.pack_int, p.pack_int, p.pack_int, p.pack_int),
			"lock": (p.pack_int, p.pack_int, p.pack_int), 
			"id": (p.pack_int,)
		}
		
		xdr_unpacker_defs={
			"write": (u.unpack_int, u.unpack_int),
			"read": (u.unpack_int, u.unpack_int, u.unpack_opaque), 
			"create_link":  (u.unpack_int, u.unpack_int, u.unpack_uint, u.unpack_uint), 
			"read_stb":(u.unpack_int, u.unpack_int), 
			"error": (u.unpack_int,)
		}
		
		return xdr_packer_defs, xdr_unpacker_defs
	
	def _setup_core_packing(self, pack, unpack):
		self.pack_type_list, self.unpack_type_list=self._core_packers[pack],self._core_unpackers[unpack]
		
	def post_init(self):
		pass
	 
	def simple_log_error(self, message, level=debug_error, file=None):
		if level <= self.debug_level:
			if file is None:
				file=sys.stderr
			print >> file, self.device_name, message
			
	def fancy_log_error(self, message, level=debug_error, file=None):
		if level <= self.debug_level:
			message=str(message).strip()
			level_str=("**INFO*****", "**ERROR****", "**WARNING**", "**DEBUG****")[level]
			if file is None:
				file=sys.stderr
			print >> file, time.asctime().strip(), '\t', level_str, '\t', self.shortname, '\t', \
				message.replace('\n','\n\t** ').replace('\r','\n\t** ')

	def log_error(self, message, level=debug_error, file=None):
		"override log_error() for sending messages to special places or formatting differently"
		self.fancy_log_error(message, level, file)
		
	def log_traceback(self, main_message='', file=None):
		exlist=traceback.format_exception(*sys.exc_info())
		s=main_message+'\n'
		for i in exlist:
			s=s+i
			
		self.log_error(s, self.debug_error, file)
	
	def log_info(self, message, file=None):
		self.log_error(message, self.debug_info, file)
	
	def log_warning(self, message, file=None):
		self.log_error(message, self.debug_warning, file)

	def log_debug(self, message, file=None):
		self.log_error(message, self.debug_all, file)

	def log_exception(self, main_message='', file=None):
		self.log_error(main_message+traceback.format_exception_only(*(sys.exc_info()[:2]))[0], self.debug_error, file)

	def __init__(self, host='127.0.0.1', device="inst0", timeout=1000, raise_on_err=None, device_name="Network Device", shortname=None,
			portmap_proxy_host=None, portmap_proxy_port=rpc.PMAP_PORT):
		
		self.raise_on_err=raise_on_err
		self.lid=None
		self.timeout=timeout
		self.device_name=device_name
		self.device_sicl_name=device
		self.host=host
		self.portmap_proxy_host=portmap_proxy_host
		self.portmap_proxy_port=portmap_proxy_port
		self.core=None
		self.abortChannel=None
		self.mux=None #default is no multiplexer active
		
		if shortname is None:
			self.shortname=device_name.strip().replace(' ','').replace('\t','')     
		else:
			self.shortname=shortname.strip().replace(' ','').replace('\t','')   
					
		if threads:
			self.threadlock=threading.RLock()
	
		try:                            
			self.reconnect()    
			
		except VXI_11_Transient_Error:
			self.log_exception("Initial connect failed... retry later")
	
	def setup_mux(self, mux=None, global_name=None):
			self.mux=mux
			self.global_mux_name=global_name
		
	def command(self, id, pack, unpack, arglist, ignore_connect=0):
		
		if not (ignore_connect or self.connected):
			raise VXI_11_Device_Not_Connected
		
		self._setup_core_packing(pack, unpack)

		try:
			result= self.core.make_call(id, arglist, self._list_packer, self._list_unpacker)
		except (RuntimeError, EOFError):
			#RuntimeError is thrown by recvfrag if the xid is off... it means we lost data in the pipe
			#EOFError is thrown if the packet isn't full length, as usually happens when ther is garbage in the pipe read as a length
			#so vacuum out the socket, and raise a transient error
			rlist=1
			ntotal=0
			while(rlist):
				rlist, wlist, xlist=select.select([self.core.sock],[],[], 1.0)
				if rlist:
					ntotal+=len(self.core.sock.recv(10000) )#get some data from it
			raise VXI_11_Stream_Sync_Lost("sync", ntotal)
		
		err=result[0]
		
		if err and self.raise_on_err:
			e=_VXI_11_enumerated_exceptions #common, correctable exceptions
			if e.has_key(err):
				raise e[err](err) #raise these exceptions explicitly
			else:
				raise VXI_11_Error(err) #raise generic VXI_11 exception
				
		return result
				
	def do_timeouts(self, timeout, lock_timeout, channel=None):
		
		if channel is None:
			channel=self.core
			
		flags=0
		if  timeout is  None:
			timeout=self.timeout
		
		if not lock_timeout and hasattr(self,"default_lock_timeout"):
			lock_timeout=self.default_lock_timeout
	
		if  lock_timeout:
			flags |=  1 # append waitlock bit
		
		if channel:
			channel.select_timeout_seconds=1.5*max(timeout, lock_timeout)/1000.0 #convert ms to sec, and be generous on hard timeout
		
		return flags, timeout, lock_timeout

	def reconnect(self): #recreate a broken connection
		"""reconnect() creates or recreates our main connection.  Useful in __init__ and in complete communications breakdowns.
		If it throws a VXI_11_Transient_Error, the connection exists, but the check_idn() handshake or post_init() failed."""
		
		self.connected=0
				
		if self.core:
			self.core.close() #if this is a reconnect, break old connection the hard way
		if self.abortChannel:
			self.abortChannel.close()
			
		self.core=rpc.TCPClient(self.host, 395183, 1, 
				portmap_proxy_host=self.portmap_proxy_host, 
				portmap_proxy_port=self.portmap_proxy_port)
				
		self._core_packers, self._core_unpackers=self._link_xdr_defs(self.core) #construct xdr data type definitions for the core
		
		err, self.lid, self.abortPort, self.maxRecvSize=self.command(
			10, "create_link","create_link", (0, 0, self.timeout, self.device_sicl_name), ignore_connect=1) #execute create_link
		
		if err: #at this stage, we always raise exceptions since there isn't any way to bail out or retry reasonably
			raise VXI_11_Error(err)
		
		self.maxRecvSize=min(self.maxRecvSize, 1048576) #never transfer more than 1MB at a shot
					
		if self.OneWayAbort:
			#self.abort_channel=OneWayAbortClient(self.host, 395184, 1, self.abortPort)
			self.abort_channel=rpc.RawUDPClient(self.host, 395184, 1, self.abortPort)
		else:
			self.abort_channel=RawTCPClient(self.host, 395184, 1, self.abortPort)
			
		connection_dict[self.lid]=(self.device_name, weakref.ref(self))

		self.locklevel=0

		self.connected=1

		self.check_idn()
		self.post_init()            


	def abort(self):
		
		self.abort_channel.select_timeout_seconds=self.timeout/1000.0 #convert to seconds
		try:
			err=self.abort_channel.make_call(1, self.lid, self.abort_channel.packer.pack_int, self.abort_channel.unpacker.unpack_int) #abort
		except EOFError:
			raise VXI_11_RPC_EOF("eof")
			
		if err and self.raise_on_err:
			raise VXI_11_Error( err)
		return err

	def disconnect(self):
		if self.connected:
			try:
				err, =self.command(23,  "id", "error", (self.lid,)) #execute destroy_link
			except:
				self.log_traceback() #if we can't close nicely, we'll close anyway
			
			self.connected=0
			del connection_dict[self.lid]
			self.lid=None
			self.core.close()
			self.abort_channel.close()
			del self.core, self.abort_channel
			self.core=None
			self.abortChannel=None
		
	def __del__(self):
		if self.lid is not None:
			self.raise_on_err=0 #no exceptions here from simple errors
			try:
				self.abort()
			except VXI_11_Error:
				pass
			try:
				self.disconnect()
			except VXI_11_Error:
				pass            


				
	def write(self, data, timeout=None, lock_timeout=0):
		"""err, bytes_sent=write(data [, timeout] [,lock_timeout]) sends data to device.  See do_timeouts() for 
		semantics of timeout and lock_timeout"""
		
		flags, timeout, lock_timeout=self.do_timeouts(timeout, lock_timeout)
		base=0
		end=len(data)
		while base<end:
			n=end-base
			if n>self.maxRecvSize:
				xfer=self.maxRecvSize
			else:
				xfer=n
				flags |= 8 #write end on last byte          
				
			err, count=self.command(11, "write", "write",  (self.lid, timeout, lock_timeout, flags, data[base:base+xfer]))
			if  err: break  
			base+=count
		return err, base
		
	def read(self, timeout=None, lock_timeout=0, count=None, termChar=None):
		"""err, reason, result=read([timeout] [,lock_timeout] [,count] [,termChar]) reads up to count bytes from the device,
		ending on count, EOI or termChar (if specified).  See do_timeouts() for semantics of the timeouts. \n
		the returned reason is an inclusive OR of 3 bits (per the VXI-11 specs section B.6.4.device_read):
			Bit 2 = END/EOI received,
			bit 1 = Terminating Character received,
			bit 0 = full requested byte count received. 
		"""
		flags, timeout, lock_timeout=self.do_timeouts(timeout, lock_timeout)

		if termChar is not None:
			flags |= 128 # append termchrset bit
			act_term=termChar
		else:
			act_term=0
		
		accumdata=""
		reason=0
		err=0
		accumlen=0
		
		while  ( (not err) and (not (reason & 4) ) and 
				( (count is None) or (accumlen < count)) and 
				( (termChar is None) or (accumdata[-1] != termChar)) ):  #wait for END flag or count or matching terminator char
			
			readcount=self.maxRecvSize/2
			if count is not None:
				readcount=min(readcount, count-accumlen)
			err, reason, data = self.command(12, "read","read", (self.lid,  readcount, timeout, lock_timeout, flags, act_term))
			accumdata+=data     
			accumlen+=len(data)
			#print err, reason, len(data), len(accumdata)
		
		return err, reason, accumdata
	
	def generic(self, code, timeout, lock_timeout):
		flags, timeout, lock_timeout=self.do_timeouts(timeout, lock_timeout)

		err, = self.command(code, "generic", "error", (self.lid, flags, timeout, lock_timeout))

		return err

	def trigger(self, timeout=None, lock_timeout=0):
		return self.generic(14, timeout, lock_timeout)

	def clear(self, timeout=None, lock_timeout=0):
		return self.generic(15, timeout, lock_timeout)
		
	def remote(self, timeout=None, lock_timeout=0):
		return self.generic(16, timeout, lock_timeout)
	
	def local(self, timeout=None, lock_timeout=0):
		return self.generic(17, timeout, lock_timeout)
	
	def read_status_byte(self, timeout=None, lock_timeout=0):
		flags, timeout, lock_timeout=self.do_timeouts(timeout, lock_timeout)

		err, status = self.command(13, "generic","read_stb", (self.lid, flags, timeout, lock_timeout))

		return err, status 
	
	def lock(self,  lock_timeout=0):
		"lock() acquires a lock on a device and the threadlock.  If it fails it leaves the connection cleanly unlocked"
		err=0
		if threads:
			self.threadlock.acquire()
		if self.locklevel==0:
			flags, timeout, lock_timeout=self.do_timeouts(0, lock_timeout)
			try:
				if self.mux: self.mux.lock_connection(self.global_mux_name)
				try:
					err, = self.command(18, "lock","error", (self.lid, flags, lock_timeout))
				except:
					if self.mux: self.mux.unlock_connection(self.global_mux_name)
					raise                   
			except:
				if threads:
					self.threadlock.release()
				raise
		
		if err:
			if threads:
				self.threadlock.release()
		else:
			self.locklevel+=1
		return err
	
	def is_locked(self):
		return self.locklevel > 0
		
	def unlock(self, priority=0):
		"""unlock(priority=0) unwinds one level of locking, and if the level is zero, really unlocks the device.
		Calls to lock() and unlock() should be matched.  If there is a danger that they are not, due to bad
		exception handling, unlock_completely() should be used as a final cleanup for a series of operations.
		Setting priority to non-zero will bias the apparent last-used time in a multiplexer (if one is used),
		so setting priority to -10 will effectively mark this channel least-recently-used, while setting it to 
		+2 will post-date the last-used time 2 seconds, so for the next 2 seconds, the device will be hard to kick
		out of the channel cache (but not impossible).
		"""
		
		self.locklevel-=1
		assert self.locklevel>=0, "Too many unlocks on device: "+self.device_name
		err=0
		try:
			if self.locklevel==0:
				try:
					err, = self.command(19, "id", "error", (self.lid,  ))   
				finally:
					if self.mux: 
						self.mux.unlock_connection(self.global_mux_name, priority) #this cannot fail, no try needed (??)
			elif priority and self.mux:
				#even on a non-final unlock, a request for changed priority is always remembered
				self.mux.adjust_priority(self.global_mux_name, priority)
		finally:            
			if threads:
				self.threadlock.release()

		return err

	def unlock_completely(self, priority=0):
		"unlock_completely() forces an unwind of any locks all the way back to zero for error cleanup.  Only exceptions thrown are fatal."
		if threads:
			self.threadlock.acquire() #make sure we have the threadlock before we try a (possibly failing) full lock
		try:
			self.lock() #just to be safe, we should already hold one level of lock!
		except VXI_11_Locked_Elsewhere: 
			pass #this is often called on error cleanup when we don't already have a lock, and we don't really care if we can't get it
		except VXI_11_Error:
			self.log_exception("Unexpected trouble locking in unlock_completely(): ")
	
		if threads:
			self.threadlock._RLock__count += (1-self.threadlock._RLock__count)
			#unwind to single lock the fast way, and make sure this variable    really existed, to shield against internal changes
		self.locklevel=1 #unwind our own counter, too           
		try:
			self.unlock(priority)
		except VXI_11_Device_Not_Locked:
			pass #if we couldn't lock above, we will probably get another exception here, and don't care
		except VXI_11_Transient_Error:
			self.log_exception("Unexpected trouble unlocking in unlock_completely(): ")
		except VXI_11_Error:
			self.log_exception("Unexpected trouble unlocking in unlock_completely(): ")
			raise
	
	def transaction(self, data, count=None, lock_timeout=0):
		"""err, reason, result=transaction(data, [, count] [,lock_timeout]) sends data and waits for a response. 
		It is guaranteed to leave the lock level at its original value on exit,
		unless KeyboardInterrupt breaks the normal flow.  If count isn't provided, there is no limit to how much data will be accepted.
		See do_timeouts() for semantics on lock_timeout."""
		
		self.lock(lock_timeout)
		reason=None
		result=None
		try:
			err,  write_count = self.write(data)
			
			if not err:
				err, reason, result = self.read(count=count)
		finally:        
			self.unlock()

		return err, reason, result

	def check_idn(self):
		'check_idn() executes "*idn?" and aborts if the result does not start with self.idn_head'
		if hasattr(self,"idn"):
			return #already done
		if hasattr(self,"idn_head") and self.idn_head is not None:

			self.lock()
			try:
				self.clear()
				err, reason, idn = self.transaction("*idn?")
				print err, reason, idn
			finally:
				self.unlock()

			check=idn.find(self.idn_head)
			self.idn=idn.strip() #save for future reference info    
			if  check:
				self.disconnect()               
				assert check==0, "Wrong device type! expecting: "+self.idn_head+"... got: "+self.idn
		else:
			self.idn="Device *idn? not checked!"
Пример #5
0
class vxi_11_connection:
    """vxi_11_connection implements handling of devices compliant with vxi11.1-vxi11.3 protocols, with which
	the user should have some familiarity"""

    debug_info = 0
    debug_error = 1
    debug_warning = 2
    debug_all = 3

    debug_level = debug_error

    OneWayAbort = 0  #by default, this class uses two-way aborts, per official vxi-11 standard

    def _list_packer(self, args):
        l = map(None, self.pack_type_list, args)  # combine lists
        for packer, data in l:
            packer(data)

    def _list_unpacker(self):
        return [func() for func in self.unpack_type_list]

    def _link_xdr_defs(self, channel):
        "self.link_xdr_defs() creates dictionaries of functions for packing and unpacking the various data types"
        p = channel.packer
        u = channel.unpacker

        xdr_packer_defs = {
            "write":
            (p.pack_int, p.pack_int, p.pack_int, p.pack_int, p.pack_opaque),
            "read": (p.pack_int, p.pack_int, p.pack_int, p.pack_int,
                     p.pack_int, p.pack_int),
            "create_link":
            (p.pack_int, p.pack_bool, p.pack_uint, p.pack_string),
            "generic": (p.pack_int, p.pack_int, p.pack_int, p.pack_int),
            "lock": (p.pack_int, p.pack_int, p.pack_int),
            "id": (p.pack_int, )
        }

        xdr_unpacker_defs = {
            "write": (u.unpack_int, u.unpack_int),
            "read": (u.unpack_int, u.unpack_int, u.unpack_opaque),
            "create_link":
            (u.unpack_int, u.unpack_int, u.unpack_uint, u.unpack_uint),
            "read_stb": (u.unpack_int, u.unpack_int),
            "error": (u.unpack_int, )
        }

        return xdr_packer_defs, xdr_unpacker_defs

    def _setup_core_packing(self, pack, unpack):
        self.pack_type_list, self.unpack_type_list = self._core_packers[
            pack], self._core_unpackers[unpack]

    def post_init(self):
        pass

    def simple_log_error(self, message, level=debug_error, file=None):
        if level <= self.debug_level:
            if file is None:
                file = sys.stderr
            print >> file, self.device_name, message

    def fancy_log_error(self, message, level=debug_error, file=None):
        if level <= self.debug_level:
            message = str(message).strip()
            level_str = ("**INFO*****", "**ERROR****", "**WARNING**",
                         "**DEBUG****")[level]
            if file is None:
                file = sys.stderr
            print >> file, time.asctime().strip(), '\t', level_str, '\t', self.shortname, '\t', \
             message.replace('\n','\n\t** ').replace('\r','\n\t** ')

    def log_error(self, message, level=debug_error, file=None):
        "override log_error() for sending messages to special places or formatting differently"
        self.fancy_log_error(message, level, file)

    def log_traceback(self, main_message='', file=None):
        exlist = traceback.format_exception(*sys.exc_info())
        s = main_message + '\n'
        for i in exlist:
            s = s + i

        self.log_error(s, self.debug_error, file)

    def log_info(self, message, file=None):
        self.log_error(message, self.debug_info, file)

    def log_warning(self, message, file=None):
        self.log_error(message, self.debug_warning, file)

    def log_debug(self, message, file=None):
        self.log_error(message, self.debug_all, file)

    def log_exception(self, main_message='', file=None):
        self.log_error(
            main_message +
            traceback.format_exception_only(*(sys.exc_info()[:2]))[0],
            self.debug_error, file)

    def __init__(self,
                 host='127.0.0.1',
                 device="inst0",
                 timeout=1000,
                 raise_on_err=None,
                 device_name="Network Device",
                 shortname=None,
                 portmap_proxy_host=None,
                 portmap_proxy_port=rpc.PMAP_PORT,
                 use_vxi_locking=True):

        self.raise_on_err = raise_on_err
        self.lid = None
        self.timeout = timeout
        self.device_name = device_name
        self.device_sicl_name = device
        self.host = host
        self.portmap_proxy_host = portmap_proxy_host
        self.portmap_proxy_port = portmap_proxy_port
        self.core = None
        self.abortChannel = None
        self.mux = None  #default is no multiplexer active
        self.use_vxi_locking = use_vxi_locking

        if shortname is None:
            self.shortname = device_name.strip().replace(' ',
                                                         '').replace('\t', '')
        else:
            self.shortname = shortname.strip().replace(' ',
                                                       '').replace('\t', '')

        if threads:
            self.threadlock = threading.RLock()

        try:
            self.reconnect()

        except VXI_11_Transient_Error:
            self.log_exception("Initial connect failed... retry later")

    def setup_mux(self, mux=None, global_name=None):
        self.mux = mux
        self.global_mux_name = global_name

    def command(self, id, pack, unpack, arglist, ignore_connect=0):

        if not (ignore_connect or self.connected):
            raise VXI_11_Device_Not_Connected

        #command has been made atomic, so that things like get_status_byte can be done
        #in a multi-threaded environment without needed a full vxi-11 lock to make it safe
        if threads:
            self.threadlock.acquire()  #make this atomic

        self._setup_core_packing(pack, unpack)

        try:
            try:
                result = self.core.make_call(id, arglist, self._list_packer,
                                             self._list_unpacker)
            except (RuntimeError, EOFError):
                #RuntimeError is thrown by recvfrag if the xid is off... it means we lost data in the pipe
                #EOFError is thrown if the packet isn't full length, as usually happens when ther is garbage in the pipe read as a length
                #so vacuum out the socket, and raise a transient error
                rlist = 1
                ntotal = 0
                while (rlist):
                    rlist, wlist, xlist = select.select([self.core.sock], [],
                                                        [], 1.0)
                    if rlist:
                        ntotal += len(
                            self.core.sock.recv(10000))  #get some data from it
                raise VXI_11_Stream_Sync_Lost("sync", ntotal)
        finally:
            if threads:
                self.threadlock.release()  #let go

        err = result[0]

        if err and self.raise_on_err:
            e = _VXI_11_enumerated_exceptions  #common, correctable exceptions
            if e.has_key(err):
                raise e[err](err)  #raise these exceptions explicitly
            else:
                raise VXI_11_Error(err)  #raise generic VXI_11 exception

        return result

    def do_timeouts(self, timeout, lock_timeout, channel=None):

        if channel is None:
            channel = self.core

        flags = 0
        if timeout is None:
            timeout = self.timeout

        if not lock_timeout and hasattr(self, "default_lock_timeout"):
            lock_timeout = self.default_lock_timeout

        if lock_timeout:
            flags |= 1  # append waitlock bit

        if channel:
            channel.select_timeout_seconds = 0.5 + 1.5 * max(
                timeout, lock_timeout
            ) / 1000.0  #convert ms to sec, and be generous on hard timeout

        return flags, timeout, lock_timeout

    def reconnect(self):  #recreate a broken connection
        """reconnect() creates or recreates our main connection.  Useful in __init__ and in complete communications breakdowns.
		If it throws a VXI_11_Transient_Error, the connection exists, but the check_idn() handshake or post_init() failed."""

        self.connected = 0

        if self.core:
            self.core.close(
            )  #if this is a reconnect, break old connection the hard way
        if self.abortChannel:
            self.abortChannel.close()

        self.core = rpc.TCPClient(self.host,
                                  395183,
                                  1,
                                  portmap_proxy_host=self.portmap_proxy_host,
                                  portmap_proxy_port=self.portmap_proxy_port)

        self._core_packers, self._core_unpackers = self._link_xdr_defs(
            self.core)  #construct xdr data type definitions for the core

        err, self.lid, self.abortPort, self.maxRecvSize = self.command(
            10,
            "create_link",
            "create_link", (0, 0, self.timeout, self.device_sicl_name),
            ignore_connect=1)  #execute create_link

        if err:  #at this stage, we always raise exceptions since there isn't any way to bail out or retry reasonably
            raise VXI_11_Error(err)

        self.maxRecvSize = min(
            self.maxRecvSize, 1048576)  #never transfer more than 1MB at a shot

        if self.OneWayAbort:
            #self.abort_channel=OneWayAbortClient(self.host, 395184, 1, self.abortPort)
            self.abort_channel = rpc.RawUDPClient(self.host, 395184, 1,
                                                  self.abortPort)
        else:
            self.abort_channel = RawTCPClient(self.host, 395184, 1,
                                              self.abortPort)

        connection_dict[self.lid] = (self.device_name, weakref.ref(self))

        self.locklevel = 0

        self.connected = 1

        self.check_idn()
        self.post_init()

    def abort(self):

        self.abort_channel.select_timeout_seconds = self.timeout / 1000.0  #convert to seconds
        try:
            err = self.abort_channel.make_call(
                1, self.lid, self.abort_channel.packer.pack_int,
                self.abort_channel.unpacker.unpack_int)  #abort
        except EOFError:
            raise VXI_11_RPC_EOF("eof")

        if err and self.raise_on_err:
            raise VXI_11_Error(err)
        return err

    def disconnect(self):
        if self.connected:
            try:
                err, = self.command(23, "id", "error",
                                    (self.lid, ))  #execute destroy_link
            except:
                self.log_traceback(
                )  #if we can't close nicely, we'll close anyway

            self.connected = 0
            del connection_dict[self.lid]
            self.lid = None
            self.core.close()
            self.abort_channel.close()
            del self.core, self.abort_channel
            self.core = None
            self.abortChannel = None

    def __del__(self):
        if self.lid is not None:
            self.raise_on_err = 0  #no exceptions here from simple errors
            try:
                self.abort()
            except VXI_11_Error:
                pass
            try:
                self.disconnect()
            except VXI_11_Error:
                pass

    def write(self, data, timeout=None, lock_timeout=0):
        """err, bytes_sent=write(data [, timeout] [,lock_timeout]) sends data to device.  See do_timeouts() for 
		semantics of timeout and lock_timeout"""

        flags, timeout, lock_timeout = self.do_timeouts(timeout, lock_timeout)
        base = 0
        end = len(data)
        while base < end:
            n = end - base
            if n > self.maxRecvSize:
                xfer = self.maxRecvSize
            else:
                xfer = n
                flags |= 8  #write end on last byte

            err, count = self.command(11, "write", "write",
                                      (self.lid, timeout, lock_timeout, flags,
                                       data[base:base + xfer]))
            if err: break
            base += count
        return err, base

    def read(self, timeout=None, lock_timeout=0, count=None, termChar=None):
        """err, reason, result=read([timeout] [,lock_timeout] [,count] [,termChar]) reads up to count bytes from the device,
		ending on count, EOI or termChar (if specified).  See do_timeouts() for semantics of the timeouts. \n
		the returned reason is an inclusive OR of 3 bits (per the VXI-11 specs section B.6.4.device_read):
			Bit 2 = END/EOI received,
			bit 1 = Terminating Character received,
			bit 0 = full requested byte count received. 
		"""
        flags, timeout, lock_timeout = self.do_timeouts(timeout, lock_timeout)

        if termChar is not None:
            flags |= 128  # append termchrset bit
            act_term = termChar
        else:
            act_term = 0

        accumdata = ""
        reason = 0
        err = 0
        accumlen = 0

        while ((not err) and (not (reason & 6)) and (
            (count is None) or
            (accumlen < count))):  #wait for END or CHR reason flag or count

            readcount = self.maxRecvSize
            if count is not None:
                readcount = min(readcount, count - accumlen)
            err, reason, data = self.command(
                12, "read", "read",
                (self.lid, readcount, timeout, lock_timeout, flags, act_term))
            accumdata += data
            accumlen += len(data)
            #print err, reason, len(data), len(accumdata)

        return err, reason, accumdata

    def generic(self, code, timeout, lock_timeout):
        flags, timeout, lock_timeout = self.do_timeouts(timeout, lock_timeout)

        err, = self.command(code, "generic", "error",
                            (self.lid, flags, timeout, lock_timeout))

        return err

    def trigger(self, timeout=None, lock_timeout=0):
        return self.generic(14, timeout, lock_timeout)

    def clear(self, timeout=None, lock_timeout=0):
        return self.generic(15, timeout, lock_timeout)

    def remote(self, timeout=None, lock_timeout=0):
        return self.generic(16, timeout, lock_timeout)

    def local(self, timeout=None, lock_timeout=0):
        return self.generic(17, timeout, lock_timeout)

    def read_status_byte(self, timeout=None, lock_timeout=0):
        flags, timeout, lock_timeout = self.do_timeouts(timeout, lock_timeout)

        err, status = self.command(13, "generic", "read_stb",
                                   (self.lid, flags, timeout, lock_timeout))

        return err, status

    def lock(self, lock_timeout=0):
        """lock() acquires a lock on a device and the threadlock.  If it fails it leaves the connection cleanly unlocked.
		If self.use_vxi_locking is false, it acquires only a thread lock locally, and does not really lock the vxi-11 device.
		This is useful if only one process is talking to a given device, and saves time."""
        err = 0
        if threads:
            self.threadlock.acquire()

        if self.use_vxi_locking and self.locklevel == 0:
            flags, timeout, lock_timeout = self.do_timeouts(0, lock_timeout)
            try:
                if self.mux: self.mux.lock_connection(self.global_mux_name)
                try:
                    err, = self.command(18, "lock", "error",
                                        (self.lid, flags, lock_timeout))
                except:
                    if self.mux:
                        self.mux.unlock_connection(self.global_mux_name)
                    raise
            except:
                if threads:
                    self.threadlock.release()
                raise

        if err:
            if threads:
                self.threadlock.release()
        else:
            self.locklevel += 1
        return err

    def is_locked(self):
        return self.locklevel > 0

    def unlock(self, priority=0):
        """unlock(priority=0) unwinds one level of locking, and if the level is zero, really unlocks the device.
		Calls to lock() and unlock() should be matched.  If there is a danger that they are not, due to bad
		exception handling, unlock_completely() should be used as a final cleanup for a series of operations.
		Setting priority to non-zero will bias the apparent last-used time in a multiplexer (if one is used),
		so setting priority to -10 will effectively mark this channel least-recently-used, while setting it to 
		+2 will post-date the last-used time 2 seconds, so for the next 2 seconds, the device will be hard to kick
		out of the channel cache (but not impossible).
		"""

        self.locklevel -= 1
        assert self.locklevel >= 0, "Too many unlocks on device: " + self.device_name

        err = 0
        try:
            if self.use_vxi_locking and self.locklevel == 0:
                try:
                    err, = self.command(19, "id", "error", (self.lid, ))
                finally:
                    if self.mux:
                        self.mux.unlock_connection(
                            self.global_mux_name,
                            priority)  #this cannot fail, no try needed (??)
            elif priority and self.mux:
                #even on a non-final unlock, a request for changed priority is always remembered
                self.mux.adjust_priority(self.global_mux_name, priority)
        finally:
            if threads:
                self.threadlock.release()

        return err

    def unlock_completely(self, priority=0):
        "unlock_completely() forces an unwind of any locks all the way back to zero for error cleanup.  Only exceptions thrown are fatal."
        if threads:
            self.threadlock.acquire(
            )  #make sure we have the threadlock before we try a (possibly failing) full lock
        try:
            self.lock(
            )  #just to be safe, we should already hold one level of lock!
        except VXI_11_Locked_Elsewhere:
            pass  #this is often called on error cleanup when we don't already have a lock, and we don't really care if we can't get it
        except VXI_11_Error:
            self.log_exception(
                "Unexpected trouble locking in unlock_completely(): ")

        if threads:
            self.threadlock._RLock__count += (1 -
                                              self.threadlock._RLock__count)
            #unwind to single lock the fast way, and make sure this variable    really existed, to shield against internal changes
        self.locklevel = 1  #unwind our own counter, too
        try:
            self.unlock(priority)
        except VXI_11_Device_Not_Locked:
            pass  #if we couldn't lock above, we will probably get another exception here, and don't care
        except VXI_11_Transient_Error:
            self.log_exception(
                "Unexpected trouble unlocking in unlock_completely(): ")
        except VXI_11_Error:
            self.log_exception(
                "Unexpected trouble unlocking in unlock_completely(): ")
            raise

    def transaction(self, data, count=None, lock_timeout=0):
        """err, reason, result=transaction(data, [, count] [,lock_timeout]) sends data and waits for a response. 
		It is guaranteed to leave the lock level at its original value on exit,
		unless KeyboardInterrupt breaks the normal flow.  If count isn't provided, there is no limit to how much data will be accepted.
		See do_timeouts() for semantics on lock_timeout."""

        self.lock(lock_timeout)
        reason = None
        result = None
        try:
            err, write_count = self.write(data)

            if not err:
                err, reason, result = self.read(count=count)
        finally:
            self.unlock()

        return err, reason, result

    def check_idn(self):
        'check_idn() executes "*idn?" and aborts if the result does not start with self.idn_head'
        if hasattr(self, "idn"):
            return  #already done
        if hasattr(self, "idn_head") and self.idn_head is not None:

            self.lock()
            try:
                self.clear()
                err, reason, idn = self.transaction("*idn?")
            finally:
                self.unlock()

            check = idn.find(self.idn_head)
            self.idn = idn.strip()  #save for future reference info
            if check:
                self.disconnect()
                assert check == 0, "Wrong device type! expecting: " + self.idn_head + "... got: " + self.idn
        else:
            self.idn = "Device *idn? not checked!"