def acquirePort(self, interfaceType=None): '''Discovers and connects to a port by waiting for a new device to be plugged in.''' #get search terms if interfaceType: searchTerm = self.getSearchTerms(interfaceType) else: searchTerm = self.getSearchTerms('genericSerial') #try to find a single port that's open. This is good for when only a single device is attached. availablePorts = self.getAvailablePorts(self.deviceScan(searchTerm)) if len(availablePorts) == 1: self.portName = availablePorts[0] return self.connect() else: notice(self.owner, "trying to acquire. Please plug me in.") newPorts = self.waitForNewPort(searchTerm, 10) #wait for new port if newPorts: if len(newPorts) > 1: notice( self.owner, 'Could not acquire. Multiple ports plugged in simultaneously.' ) return False else: self.portName = newPorts[0] return self.connect() else: return False
def initAfterSet(self): # gets called once interface is set into shell. self.receiveSocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) # UDP, would be socket.SOCK_STREAM for TCP self.receiveSocket.bind((self.receiveIPAddress, self.receiveIPPort)) # bind to socket notice(self, "opened socket on " + str(self.receiveIPAddress) + " port " + str(self.receiveIPPort)) self.transmitSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def __init__(self, inputMatrices): if type(inputMatrices) != list: # make sure that input is a list notice(self, "expected input of type list but instead got type " + str(list)) return False self.arrays = inputMatrices self.lengths = [array.length for array in inputMatrices] # stores lengths of each array self.length = sum(self.lengths)
def getSearchTerms(self, interfaceType): '''returns the likely prefix for a serial port based on the operating system and device type''' #define search strings in format {'OperatingSystem':'SearchString'} ftdi = {'Darwin': 'tty.usbserial-'} lufa = {'Darwin': 'tty.usbmodem'} genericSerial = {'Darwin': 'tty.'} searchStrings = { 'ftdi': ftdi, 'lufa': lufa, 'genericSerial': genericSerial } opSys = platform.system() #nominally detects the system if interfaceType in searchStrings: if opSys in searchStrings[interfaceType]: return searchStrings[interfaceType][opSys] else: notice( 'getSearchTerm', 'operating system support not found for interface type ' + interfaceType) return False else: notice( 'getSearchTerm', 'interface support not found for interface type ' + interfaceType) return False
def transmit(self, data): '''Sends request for data to be transmitted over the serial port. Format is as a list.''' if self.isConnected: self.transmitQueue.put( data) #converts to list in case data comes in as a string. else: notice(self, 'serialInterface is not connected.')
def init(self, axesSteps, accelSteps = 0, decelSteps = 0, accelRate = 0, external = False, sync = False, majorSteps = None): # axesSteps: a list containing the number of steps which each axis of the node should take in synchrony. # accelSteps: number of virtual major axis steps during which acceleration should occur. # deccelSteps: number of virtual major axis steps during which deceleration should occur. # accelRate: accel/decel rate in steps/sec^2 # external: indicates whether the actionObject will be commited to its interface externally. # When False, the actionObject will self commit and release. # When True, the actionObject will prepare a packet but will need to be commited and release externally. # sync: indicates that this actionObject will be synchronized externally and that parameters might change. This will prevent it from spawning. # majorSteps: this parameter is only called internally when a request calls for too many steps for one actionObject and the actionObject needs to spawn. if type(axesSteps) != list and type(axesSteps) != tuple: axesSteps = [axesSteps] self.axesSteps = [int(axis) for axis in axesSteps] #list of steps to take in each axis e.g. [x, y, z]. May be provided as a tuple. self.accelSteps = accelSteps self.decelSteps = decelSteps self.accelRate = accelRate self.external = external self.sync = sync self.actionSequence = False #start out with no action sequence self.sequenceMajorSteps = None #if this actionObject becomes an actionSequence parent, this will store the list of major axes #calculate virtual major axis if majorSteps: #if provided externally, use that. self.majorSteps = majorSteps else: #generate majorSteps from provided axes. self.majorSteps = max([abs(axis) for axis in self.axesSteps]) if self.majorSteps > self.virtualNode.maxSteps: #check if step request exceeds maximum length if not sync: #no other anticipated recalculations, so go ahead and generate actionSequence #need to generate an actionSequence with multiple new actionObjects self.actionSequence = self.actionSequenceGen() return self.actionSequence else: #the majorSteps has not yet been synchronized, so cannot generate an action sequence yet. return self else: #step request is executable by this action object. Either initial request met this criteria, or this actionObject was initialized by actionSequence(). #calculate directions directions = [1 if axis>0 else 0 for axis in self.axesSteps] directionMultiplexer = [2**b for b in range(self.virtualNode.numberOfAxes-1, -1, -1)] #generates [2^n, 2^n-1,...,2^0] self.directionByte = sum(map(lambda x,y: x*y, directionMultiplexer, directions)) #directionByte is each term of directionMultiplexer multiplied by the directions and then summed. if external or sync: #actionObject is being managed by an external function and shouldn't auto-commit return self else: #set packet accelRate = self.tbAccelRate(self.accelRate) #convert accel rate into timebase self.setPacket({'majorSteps':self.majorSteps, 'directions':self.directionByte, 'steps':abs(self.axesSteps[0]), 'accel':accelRate, 'accelSteps':accelSteps, 'decelSteps':decelSteps}) self.commitAndRelease() self.waitForChannelAccess() moveQueued = False while not moveQueued: if self.transmitPersistent(): responsePacket = self.getPacket() moveQueued = bool(responsePacket['statusCode']) #False if move was not queued time.sleep(0.02) else: notice(self.virtualNode, "got no response to spin request") return False return responsePacket
def setTimeout(self, timeout): '''Sets timeout for receiving on port.''' if self.port: try: self.port.timeout = timeout except: notice(self, "could not set timeout: " + sys.exc_info()[0])
def acquirePort(self, interfaceType=None): """Discovers and connects to a port by waiting for a new device to be plugged in.""" # get search terms if interfaceType: searchTerm = self.getSearchTerms(interfaceType) else: searchTerm = self.getSearchTerms("genericSerial") # try to find a single port that's open. This is good for when only a single device is attached. availablePorts = self.getAvailablePorts(self.deviceScan(searchTerm)) if len(availablePorts) == 1: self.portName = availablePorts[0] return self.connect() else: notice(self.owner, "trying to acquire. Please plug me in.") newPorts = self.waitForNewPort(searchTerm, 10) # wait for new port if newPorts: if len(newPorts) > 1: notice(self.owner, "Could not acquire. Multiple ports plugged in simultaneously.") return False else: self.portName = newPorts[0] return self.connect() else: return False
def channelAccess(self): '''This gets called when channel access is granted. To prevent double-transmission, only transmit if external is True.''' if self.external: #set packet accelRate = self.tbAccelRate( self.accelRate) #convert accel rate into timebase self.setPacket({ 'majorSteps': self.majorSteps, 'directions': self.directionByte, 'steps': abs(self.axesSteps[0]), 'accel': accelRate, 'accelSteps': self.accelSteps, 'decelSteps': self.decelSteps, 'sync': int(bool(self.sync)) }) #since this node was instantiated under external control, it did not auto-transmit. moveQueued = False while not moveQueued: if self.transmitPersistent(): responsePacket = self.getPacket() print responsePacket moveQueued = bool( responsePacket['statusCode'] ) #False if move was not queued, meaning buffer is full if not moveQueued: time.sleep(0.02) #wait before retransmitting else: notice(self.virtualNode, "got no response to spin request")
def __getattr__(self, attribute): """ Forwards all unsupported calls to the parent actionObject.""" if hasattr(self.parent, attribute): # parent actionObject contains requested attribute return getattr(self.parent, attribute) else: notice(self, "ActionObject DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute)
def nodeFunctionCall(self, node, attribute, args, kwargs): if hasattr(node, attribute): return getattr(node, attribute)(*list(args), **kwargs) else: notice(self.compoundNode, "NODE DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute)
def setTimeout(self, timeout): """Sets timeout for receiving on port.""" if self.port: try: self.port.timeout = timeout except: notice(self, "could not set timeout: " + sys.exc_info()[0])
def __init__(self, inputMatrices): if type(inputMatrices) != list: #make sure that input is a list notice(self, 'expected input of type list but instead got type ' + str(list)) return False self.arrays = inputMatrices self.lengths = [array.length for array in inputMatrices] #stores lengths of each array self.length = sum(self.lengths)
def __init__(self, *args, **kwargs): if 'name' in kwargs: #set name as provided self.name = kwargs['name'] else: #set name to filename for generating proper notice commands self.name = inspect.getfile(self.__class__) if 'persistenceFile' in kwargs: #set persistence file as provided self.persistenceFilename = kwargs['persistenceFile'] if 'name' not in kwargs: #If no name is provided, and multiple machines share the persistence file, this can result in a namespace conflict. notice(self, 'Warning: setting persistence without providing a name to the virtual machine can result in a conflict in multi-machine persistence files.') else: self.persistenceFilename = None self.persistence = utilities.persistenceManager(filename = self.persistenceFilename, namespace = self.name) if 'interface' in kwargs: self.providedInterface = kwargs['interface'] #interface provided on instantiation of virtual machine. else: self.providedInterface = None self.publishEnabled = True #this should get set explicitly to false in user init by calling disablePublishing #run user initialization self.init(*args, **kwargs) #calls child class init function self.initInterfaces() self.initControllers() self.initCoordinates() self.initKinematics() self.initFunctions() self.initPublish() self.initLast() self.publish()
def functionCall(callObject, attribute, args, kwargs): """Calls callObject.attribute(*args, **kwargs)""" if hasattr(callObject, attribute): return getattr(callObject, attribute)(*list(args), **kwargs) else: notice(callObject, "actionObject DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute)
def init(self): self.commitAndRelease() #commit self immediately self.waitForChannelAccess() if self.transmitPersistent(): return self.getPacket()['URL'] else: notice(self.virtualNode, 'NO URL RECEIVED') return False
def init(self): self.setPacket({}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): return self.getPacket() else: notice(self.virtualNode, 'unable to get status from node.')
def functionCall(callObject, attribute, args, kwargs): '''Calls callObject.attribute(*args, **kwargs)''' if hasattr(callObject, attribute): return getattr(callObject, attribute)(*list(args), **kwargs) else: notice(callObject, "actionObject DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute)
def __getattr__(self, attribute): ''' Forwards all unsupported calls to the parent actionObject.''' if hasattr( self.parent, attribute): #parent actionObject contains requested attribute return getattr(self.parent, attribute) else: notice(self, "ActionObject DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute)
def set(self, valueArray): if len(valueArray) != len(self.baseList): notice(self, 'input array length must match coordinate length.') return False else: for index, item in enumerate(valueArray): #any None input will not modify value self.baseList[index] = coordinates.uFloat(item, self.baseList[index].units) if item != None else self.baseList[index] return True
def init(self, IP): self.setPacket({'setAddress':IP}, mode = 'multicast') self.commitAndRelease() #commit self immediately self.waitForChannelAccess(5) if self.transmitPersistent(timeout = 15): time.sleep(1) #debounce for button press return self.getPacket()['URL'] else: notice(self.virtualNode, 'TIMEOUT WAITING FOR BUTTON PRESS')
def transmit(self): """Sends a packet over the interface to the matching physical node. Note that this method will only be called within the interface channelAccess thread, which guarantees that the channel is avaliable.""" if self.channelAccessGranted.is_set(): self.interface.transmit( virtualNode=self.virtualNode, port=self.port, packetSet=self.packetSet, mode=self.mode ) else: notice(self.virtualNode, "tried to transmit without channel access!")
def __init__(self, *nodes): self.nodes = nodes self.nodeCount = len(self.nodes) self.name = "[" + ''.join([str(node.name) + "," for node in nodes])[:-1] + "]" interfaces = [node.interface.Interface for node in nodes] #nodes have an interface shell if all(interface == interfaces[0] for interface in interfaces): self.commonInterface = True else: self.commonInterface = False notice(self, "warning: not all members of compound node share a common interface!")
def init(self): self.setPacket({}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "LED toggled") return True else: notice(self.virtualNode, "halp doesn't toggle") return False
def init(self, temp): self.setPacket({'temp':temp}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "setting tempeature") return True else: notice(self.virtualNode, "couldn't set temperature") return False
def run(self): """Code run by the transmit thread.""" while True: transmitState, transmitPacket = self.getTransmitPacket() if transmitState: if self.port: self.port.write(serialize(transmitPacket)) else: notice(self, "Cannot Transmit - No Serial Port Initialized") time.sleep(0.0005)
def init(self, temp): self.setPacket({}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "extruding at bla per second") return True else: notice(self.virtualNode, "can't extrude") return False
def init(self): self.setPacket({}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "Stepper motor disabled.") return True else: notice(self.virtualNode, "Stepper motor could not be disabled.") return False
def transmitPersistent(self, tries=10, timeout=0.2): """Transmit a packet until a response is received.""" for i in range(tries): self.transmit() if self.waitForResponse(timeout): return True notice( self.virtualNode, "Could not reach virtual node. Retrying (#" + str(i + 2) + ")" ) # i starts at 0, and when this gets called already tried once. return False
def init(self, temp): self.setPacket({'temp': temp}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "setting tempeature") return True else: notice(self.virtualNode, "couldn't set temperature") return False
def initAfterSet(self): #gets called once interface is set into shell. self.receiveSocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM) #UDP, would be socket.SOCK_STREAM for TCP self.receiveSocket.bind( (self.receiveIPAddress, self.receiveIPPort)) #bind to socket notice( self, "opened socket on " + str(self.receiveIPAddress) + " port " + str(self.receiveIPPort)) self.transmitSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
def transmitPersistent(self, tries=10, timeout=0.2): '''Transmit a packet until a response is received.''' for i in range(tries): self.transmit() if self.waitForResponse(timeout): return True notice( self.virtualNode, 'Could not reach virtual node. Retrying (#' + str(i + 2) + ')' ) #i starts at 0, and when this gets called already tried once. return False
def init(self): self.setPacket({}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "getting temperature") responsePacket = self.getPacket() return responsepacket['response'] #see initpackets else: notice(self.virtualNode, "can't read temperature") return False
def __getattr__(self, attribute): ''' Forwards any unsupported calls to the shell onto the node.''' if self.hasNode(): #Shell contains a node. if hasattr(self.node, attribute): #node contains requested attribute return getattr(self.node, attribute) else: notice(self, "NODE DOESN'T HAVE REQUESTED ATTRIBUTE") raise AttributeError(attribute) else: notice(self, "NODE IS NOT INITIALIZED") raise AttributeError(attribute)
def transmit(self): '''Sends a packet over the interface to the matching physical node. Note that this method will only be called within the interface channelAccess thread, which guarantees that the channel is avaliable.''' if self.channelAccessGranted.is_set(): self.interface.transmit(virtualNode=self.virtualNode, port=self.port, packetSet=self.packetSet, mode=self.mode) else: notice(self.virtualNode, 'tried to transmit without channel access!')
def set(self, valueArray): if len(valueArray) != len(self.baseList): notice(self, "input array length must match coordinate length.") return False else: for index, item in enumerate(valueArray): # any None input will not modify value self.baseList[index] = ( coordinates.uFloat(item, self.baseList[index].units) if item != None else self.baseList[index] ) return True
def init(self, a, b): self.setPacket({'a':a, 'b':b}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): notice(self.virtualNode, "adding") responsePacket = self.getPacket() return responsePacket['response'] #set by the initpackets routine else: notice(self.virtualNode, "computer says no") return False
def run(self): '''Code run by the transmit thread.''' while True: transmitState, transmitPacket = self.getTransmitPacket() if transmitState: if self.port: self.port.write(serialize(transmitPacket)) else: notice(self, 'Cannot Transmit - No Serial Port Initialized') time.sleep(0.0005)
def init(self, velocity): #velocity is in steps/sec velocity = int(round((velocity * self.virtualNode.uStepsPerStep * self.virtualNode.timeBasePeriod)/16.0,0)) #convert into 16*uSteps/timebase print velocity self.setPacket({'velocity': velocity}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): return True else: notice(self.virtualNode, 'unable to set velocity.') return False
def loadNodeFromFile(self, filename, **kwargs): ''' Loads a node into the node shell from a provided filename. Assumes that this is called from a node shell that has defined self.name''' try: self.setNode(imp.load_source('', filename).virtualNode(**kwargs)) notice(self, "loaded node from: " + filename) return True except IOError, error: notice(self, "error loading file.") print error return False
def loadNodeFromModule(self, module, **kwargs): '''Loads a node into the node shell from the provided class. Note that class itself should be provided, NOT a class instance.''' try: if hasattr(module, 'virtualNode'): self.setNode(module.virtualNode(**kwargs)) else: self.setNode(module(**kwargs)) notice(self, "loaded module " + str(module.__name__)) return True except AttributeError, err: notice(self, "unable to load module: " + str(err)) return False
def connectToPort(self, portName): """Actually connects the interface to the port""" try: self.port = serial.Serial(portName, self.baudRate, timeout=self.timeOut) self.port.flushInput() self.port.flushOutput() notice(self, "port " + str(portName) + " connected succesfully.") time.sleep(2) # some serial ports require a brief amount of time between opening and transmission self.isConnected = True self.startTransmitter() return True except: notice(self, "error opening serial port " + str(portName)) return False
def init(self, velocity): #velocity is in steps/sec velocity = int( round((velocity * self.virtualNode.uStepsPerStep * self.virtualNode.timeBasePeriod) / 16.0, 0)) #convert into 16*uSteps/timebase print velocity self.setPacket({'velocity': velocity}) self.commitAndRelease() self.waitForChannelAccess() if self.transmitPersistent(): return True else: notice(self.virtualNode, 'unable to set velocity.') return False
def connect(self, portName=None): """Locates port for interface on host machine.""" # check if port has been provided explicitly to connect function if portName: self.connectToPort(portName) return True # port hasn't been provided to connect function, check if assigned on instantiation elif self.portName: self.connectToPort(self.portName) return True # no port name provided else: notice(self, "no port name provided.") return False
def connect(self, portName=None): '''Locates port for interface on host machine.''' #check if port has been provided explicitly to connect function if portName: self.connectToPort(portName) return True #port hasn't been provided to connect function, check if assigned on instantiation elif self.portName: self.connectToPort(self.portName) return True #no port name provided else: notice(self, 'no port name provided.') return False
def channelAccess(self): '''This gets called when channel access is granted. To prevent double-transmission, only transmit if external is True.''' if self.external: #set packet accelRate = self.tbAccelRate(self.accelRate) #convert accel rate into timebase self.setPacket({'majorSteps':self.majorSteps, 'directions':self.directionByte, 'steps':abs(self.axesSteps[0]), 'accel':accelRate, 'accelSteps':self.accelSteps, 'decelSteps':self.decelSteps, 'sync':int(bool(self.sync))}) #since this node was instantiated under external control, it did not auto-transmit. moveQueued = False while not moveQueued: if self.transmitPersistent(): responsePacket = self.getPacket() print responsePacket moveQueued = bool(responsePacket['statusCode']) #False if move was not queued, meaning buffer is full if not moveQueued: time.sleep(0.02) #wait before retransmitting else: notice(self.virtualNode, "got no response to spin request")
def getSearchTerms(self, interfaceType): """returns the likely prefix for a serial port based on the operating system and device type""" # define search strings in format {'OperatingSystem':'SearchString'} ftdi = {"Darwin": "tty.usbserial-"} lufa = {"Darwin": "tty.usbmodem"} genericSerial = {"Darwin": "tty."} searchStrings = {"ftdi": ftdi, "lufa": lufa, "genericSerial": genericSerial} opSys = platform.system() # nominally detects the system if interfaceType in searchStrings: if opSys in searchStrings[interfaceType]: return searchStrings[interfaceType][opSys] else: notice("getSearchTerm", "operating system support not found for interface type " + interfaceType) return False else: notice("getSearchTerm", "interface support not found for interface type " + interfaceType) return False