def __init__(self): """ Makes a new AHF_CageSet object by loading from AHFconfig.jsn or by querying the user Either reads a dictionary from a config file, AHFconfig.jsn, in the same directory in which the program is run, or if the file is not found, it querries the user for settings and then writes a new file. """ try: with open ('AHFconfig.jsn', 'r') as fp: data = fp.read() configDict = json.loads(data) fp.close() self.cageID = configDict.get('Cage ID') self.headFixer = configDict.get('Head Fixer') AHF_HeadFixer.get_class (self.headFixer).configDict_read (self, configDict) self.rewardPin = int(configDict.get('Reward Pin')) self.tirPin = int(configDict.get('Tag In Range Pin')) self.contactPin = int (configDict.get ('Contact Pin')) self.contactPolarity = configDict.get ('Contact Polarity') # RISING or FALLING, GPIO.RISING = 31, GPIO.FALLING = 32 self.contactPUD = configDict.get ('Contact Pull Up Down') # OFF, DOWN, or UP, GPIO.PUD_OFF=20, GPIO.PUD_DOWN =21, GPIO.PUD_UP=22 self.ledPin = int (configDict.get ('LED Pin')) self.serialPort = configDict.get ('Serial Port') self.dataPath =configDict.get ('Path to Save Data') self.mouseConfigPath = configDict.get ('Mouse Config Path') except (TypeError, IOError) as e: #we will make a file if we didn't find it, or if it was incomplete print ('Unable to open base configuration file, AHFconfig.jsn, let\'s make a new one.\n') self.cageID = input('Enter the cage ID:') self.headFixer = AHF_HeadFixer.get_HeadFixer_from_user() AHF_HeadFixer.get_class (self.headFixer).config_user_get (self) self.rewardPin = int (input ('Enter the GPIO pin connected to the water delivery solenoid:')) self.tirPin = int (input ('Enter the GPIO pin connected to the Tag-in-Range pin on the Tag reader:')) self.contactPin = int (input ('Enter the GPIO pin connected to the headbar contacts or IR beam-breaker:')) contactInt = int (input ('Enter the contact polarity, 0=FALLING for IR beam-breaker or falling polarity electrical contacts, 1=RISING for rising polarity elctrical contacts:')) if contactInt == 0: self.contactPolarity = 'FALLING' else: self.contactPolarity = 'RISING' contactInt = int (input('Enter desired resistor on contact pin, 0=OFF if external resistor present, else 1=DOWN if rising polarity electrical contact or 2 = UP if IR beam-breaker or falling polarity electrical contacts:')) if contactInt ==0: self.contactPUD = 'OFF' elif contactInt == 1: self.contactPUD = 'DOWN' else: self.contactPUD='UP' self.ledPin = int (input ('Enter the GPIO pin connected to the blue LED for camera illumination:')) self.serialPort = input ('Enter serial port for tag reader(likely either /dev/ttyAMA0 or /dev/ttyUSB0):') self.dataPath = input ('Enter the path to the directory where the data will be saved:') self.mouseConfigPath = input ('Enter the path to the directory where mouse configuration data is located:') self.show() doSave = input ('Enter \'e\' to re-edit the new Cage settings, or any other character to save the new settings to a file.') if doSave == 'e' or doSave == 'E': self.edit() else: self.save() return
def edit (self): """ Allows the user to edit and save the cage settings """ while True: self.show() editNum = int(input ('Enter number of paramater to Edit, or 0 when done to save file:')) if editNum == 0: break elif editNum == 1: self.cageID = input('Enter the cage ID:') elif editNum == 2: self.headFixer = AHF_HeadFixer.get_HeadFixer_from_user() AHF_HeadFixer.get_class (self.headFixer).config_user_get (self) elif editNum == 3: AHF_HeadFixer.get_class (self.headFixer).config_user_get (self) elif editNum == 4: self.rewardPin = int (input ('Enter the GPIO pin connected to the water delivery solenoid:')) elif editNum == 5: self.tirPin= int (input ('Enter the GPIO pin connected to the Tag-in-Range pin on the Tag reader:')) elif editNum == 6: self.contactPin= int (input ('Enter the GPIO pin connected to the headbar contacts or IR beam-breaker:')) elif editNum == 7: contactInt = int (input ('Enter the contact polarity, 0=FALLING for IR beam-breaker or falling polarity electrical contacts, 1=RISING for rising polarity elctrical contacts:')) if contactInt == 0: self.contactPolarity = 'FALLING' else: self.contactPolarity = 'RISING' contactInt = int (input('Enter desired resistor on contact pin, 0=OFF if external resistor present, else 1=DOWN if rising polarity electrical contact or 2 = UP if IR beam-breaker or falling polarity electrical contacts:')) if contactInt ==0: self.contactPUD = 'OFF' elif contactInt == 1: self.contactPUD = 'DOWN' else: self.contactPUD='UP' elif editNum == 8: self.ledPin = int (input ('Enter the GPIO pin connected to the blue LED for camera illumination:')) elif editNum == 9: self.serialPort = input ('Enter serial port for tag reader(likely either /dev/ttyAMA0 or /dev/ttyUSB0):') elif editNum == 10: self.dataPath = input ('Enter the path to the directory where the data will be saved:') elif editNum == 11: self.mouseConfigPath = input ('Enter the path to the directory where mouse configuration data is located:') else: print ('I don\'t recognize that number ' + str (editNum)) self.show() self.save() return
def show(self): """ Prints the current configuration stored in this AHF_CageSet to the console, nicely formatted :param: none :returns: nothing """ print( '****************Current Auto-Head-Fix Cage Settings********************************' ) print('1:Cage ID=' + str(self.cageID)) print('2:Head Fix method=' + self.headFixer) print('3:' + AHF_HeadFixer.get_class(self.headFixer).config_show(self)) print('4:Reward Solenoid Pin=' + str(self.rewardPin)) print('5:Tag-In-Range Pin=' + str(self.tirPin)) print('6:Contact Pin=' + str(self.contactPin)) print('7:Contact Polarity =' + str(self.contactPolarity) + ' and contact Pull Up Down = ' + str(self.contactPUD)) print('8:Brain LED Illumination Pin=' + str(self.ledPin)) print('9:Tag Reader serialPort=' + self.serialPort) print('10:dataPath=' + self.dataPath) print('11:Has Entry Beam Break=' + str(self.hasEntryBB)) if self.hasEntryBB == True: print('12:Entry Beam Break Pin=' + str(self.entryBBpin)) print( '**************************************************************************************' )
def save(self): """ Saves current configuration stored in this AHF_CageSet object into the file ./AHFconfig.jsn Call this function after modifying the contents of the AHF_CageSet to save your changes :param: none :returns: nothing """ jsonDict={'Cage ID':self.cageID,'Head Fixer':self.headFixer} AHF_HeadFixer.get_class (self.headFixer).configDict_set (self, jsonDict) jsonDict.update ({'Reward Pin':self.rewardPin, 'Tag In Range Pin':self.tirPin, 'Contact Pin':self.contactPin}) jsonDict.update ({'Contact Polarity':self.contactPolarity, 'Contact Pull Up Down':self.contactPUD}) jsonDict.update ({'LED Pin' : self.ledPin, 'Serial Port' : self.serialPort, 'Path to Save Data':self.dataPath, 'Mouse Config Path':self.mouseConfigPath}) with open ('AHFconfig.jsn', 'w') as fp: fp.write (json.dumps (jsonDict)) fp.close () uid = pwd.getpwnam ('pi').pw_uid gid = grp.getgrnam ('pi').gr_gid os.chown ('AHFconfig.jsn', uid, gid) # we may run as root for pi PWM, so we need to expicitly set ownership
def hardwareTester(): """ Hardware Tester for Auto Head Fixing, allows you to verify the various hardware bits are working """ cageSet = AHF_CageSet() # set up GPIO to use BCM mode for GPIO pin numbering GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(cageSet.rewardPin, GPIO.OUT) GPIO.setup(cageSet.ledPin, GPIO.OUT) GPIO.setup(cageSet.tirPin, GPIO.IN) # contact pin can have a pullup/down resistor enabled GPIO.setup(cageSet.contactPin, GPIO.IN, pull_up_down=getattr(GPIO, "PUD_" + cageSet.contactPUD)) # initialize head fixer headFixer = AHF_HeadFixer.get_class(cageSet.headFixer)(cageSet) # open TagReader try: tagReader = AHF_TagReader(cageSet.serialPort, True) except IOError: tagReader = None htloop(cageSet, tagReader, headFixer)
def main(): """ The main function for the AutoHeadFix program. It initializes or loads settings and configurations, then endlessly loops running entries and head fix trials Ctrl-C is used to enter a menu-driven mode where settings can be altered. """ try: # load general settings for this cage, mostly hardware pinouts # things not expected to change often - there is only one AHFconfig.jsn file, in the enclosing folder cageSettings = AHF_CageSet() #print (cageSettings) # get settings that may vary by experiment, including rewarder, camera parameters, and stimulator # More than one of these files can exist, and the user needs to choose one or make one # we will add some other variables to expSettings so we can pass them as a single argument to functions # logFP, statsFP, dateStr, dayFolderPath, doHeadFix, # configFile can be specified if launched from command line, eg, sudo python3 myconfig or sudo python3 AHFexp_myconfig.jsn configFile = None if argv.__len__() > 1: configFile = argv[1] expSettings = AHF_Settings(configFile) # nextDay starts tomorrow at KDAYSTARTHOUR #nextDay = (int((time() - timezone)/KSECSPERDAY) + 1) * KSECSPERDAY + timezone + (KDAYSTARTHOUR * KSECSPERHOUR) nextDay = ((int( (time() - timezone + localtime().tm_isdst * 3600) / KSECSPERDAY)) * KSECSPERDAY) + timezone - (localtime().tm_isdst * 3600 ) + KSECSPERDAY + KDAYSTARTHOUR # Create folders where the files for today will be stored makeDayFolderPath(expSettings, cageSettings) # initialize mice with zero mice mice = Mice() # make daily Log files and quick stats file makeLogFile(expSettings, cageSettings) makeQuickStatsFile(expSettings, cageSettings, mice) #Generate h5 file to store mouse-individual data makeH5File(expSettings, cageSettings, mice) updateStats(expSettings.statsFP, mice) backup_H5(expSettings, cageSettings) # set up the GPIO pins for each for their respective functionalities. GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(cageSettings.ledPin, GPIO.OUT, initial=GPIO.LOW) # turns on brain illumination LED GPIO.setup(cageSettings.led2Pin, GPIO.OUT, initial=GPIO.LOW) # turns on masking stim LED2 GPIO.setup(cageSettings.tirPin, GPIO.IN) # Tag-in-range output from RFID tag reader GPIO.setup(cageSettings.contactPin, GPIO.IN, pull_up_down=getattr(GPIO, "PUD_" + cageSettings.contactPUD)) if cageSettings.contactPolarity == 'RISING': expSettings.contactEdge = GPIO.RISING expSettings.noContactEdge = GPIO.FALLING expSettings.contactState = GPIO.HIGH expSettings.noContactState = GPIO.LOW else: expSettings.contactEdge = GPIO.FALLING expSettings.noContactEdge = GPIO.RISING expSettings.contactState = GPIO.LOW expSettings.noContactState = GPIO.HIGH # make head fixer - does its own GPIO initialization from info in cageSettings headFixer = AHF_HeadFixer.get_class( cageSettings.headFixer)(cageSettings) # make a rewarder rewarder = AHF_Rewarder(30e-03, cageSettings.rewardPin) rewarder.addToDict('entrance', expSettings.entranceRewardTime) rewarder.addToDict('task', expSettings.taskRewardTime) # make a notifier object if expSettings.hasTextMsg == True: notifier = AHF_Notifier(cageSettings.cageID, expSettings.phoneList) else: notifier = None # make RFID reader tagReader = TagReader(cageSettings.serialPort, False, None) # configure camera camera = AHF_Camera(expSettings.camParamsDict) # make UDP Trigger if expSettings.hasUDP == True: UDPTrigger = AHF_UDPTrig(expSettings.UDPList) print(UDPTrigger) else: UDPTrigger = None # make a lick detector simpleLogger = Simple_Logger(expSettings.logFP) lickDetector = AHF_LickDetector((0, 1), 26, simpleLogger) sleep(1) lickDetector.start_logging() # make stimulator(s) stimulator = [ AHF_Stimulator.get_class(i)(cageSettings, expSettings, rewarder, lickDetector, camera) for i in expSettings.stimulator ] #Stimdict is chosen from the first stimulator expSettings.stimDict = stimulator[0].configDict # Entry beam breaker if cageSettings.hasEntryBB == True: GPIO.setup(cageSettings.entryBBpin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.add_event_detect(cageSettings.entryBBpin, GPIO.BOTH, entryBBCallback) #GPIO.add_event_callback (cageSettings.entryBBpin, entryBBCallback) gTubePanicTime = time() + 25920000 # a month from now. gTubeMaxTime = expSettings.inChamberTimeLimit except Exception as anError: print('Unexpected error starting AutoHeadFix:', str(anError)) raise anError exit(0) try: print('Waiting for a mouse...') while True: #start main loop try: # wait for mouse entry, with occasional timeout to catch keyboard interrupt GPIO.wait_for_edge( cageSettings.tirPin, GPIO.RISING, timeout=kTIMEOUTmS ) # wait for entry based on Tag-in-range pin if (GPIO.input(cageSettings.tirPin) == GPIO.HIGH): try: tag = tagReader.readTag() except (IOError, ValueError): tagReader.clearBuffer() continue entryTime = time() if cageSettings.hasEntryBB == True: GPIO.remove_event_detect(cageSettings.entryBBpin) thisMouse = mice.getMouseFromTag(tag) if thisMouse is None: # try to open mouse config file to initialize mouse data thisMouse = Mouse(tag, 1, 0, 0, 0, 0, 0) mice.addMouse(thisMouse, expSettings.statsFP) writeToLogFile(expSettings.logFP, thisMouse, 'entry') thisMouse.entries += 1 # if we have entrance reward, first wait for entrance reward or first head-fix, which countermands entry reward if thisMouse.entranceRewards < expSettings.maxEntryRewards: giveEntranceReward = True expSettings.doHeadFix = expSettings.propHeadFix > random( ) while GPIO.input( cageSettings.tirPin) == GPIO.HIGH and time( ) < (entryTime + expSettings.entryRewardDelay): GPIO.wait_for_edge(cageSettings.contactPin, expSettings.contactEdge, timeout=kTIMEOUTmS) if (GPIO.input(cageSettings.contactPin) == expSettings.contactState): runTrial(thisMouse, expSettings, cageSettings, rewarder, headFixer, stimulator[thisMouse.stimType], UDPTrigger) giveEntranceReward = False break if (GPIO.input(cageSettings.tirPin) == GPIO.HIGH) and giveEntranceReward == True: thisMouse.reward( rewarder, 'entrance' ) # entrance reward was not countermanded by an early headfix writeToLogFile(expSettings.logFP, thisMouse, 'entryReward') # wait for contacts and run trials till mouse exits or time in chamber exceeded expSettings.doHeadFix = expSettings.propHeadFix > random() while GPIO.input( cageSettings.tirPin) == GPIO.HIGH and time( ) < entryTime + expSettings.inChamberTimeLimit: if (GPIO.input(cageSettings.contactPin) == expSettings.noContactState): GPIO.wait_for_edge(cageSettings.contactPin, expSettings.contactEdge, timeout=kTIMEOUTmS) if (GPIO.input(cageSettings.contactPin) == expSettings.contactState): runTrial(thisMouse, expSettings, cageSettings, rewarder, headFixer, stimulator[thisMouse.stimType], UDPTrigger) expSettings.doHeadFix = expSettings.propHeadFix > random( ) # set doHeadFix for next contact # either mouse left the chamber or has been in chamber too long if GPIO.input(cageSettings.tirPin) == GPIO.HIGH and time( ) > entryTime + expSettings.inChamberTimeLimit: # explictly turn off pistons, though they should be off at end of trial headFixer.releaseMouse() if expSettings.hasTextMsg == True: notifier.notify(thisMouse.tag, (time() - entryTime), True) # wait for mouse to leave chamber, with no timeout, unless it left while we did last 3 lines if GPIO.input(cageSettings.tirPin) == GPIO.HIGH: GPIO.wait_for_edge(cageSettings.tirPin, GPIO.FALLING) if expSettings.hasTextMsg == True: notifier.notify(thisMouse.tag, (time() - entryTime), False) tagReader.clearBuffer() if cageSettings.hasEntryBB == True: #GPIO.setup (cageSettings.entryBBpin, GPIO.IN, pull_up_down = GPIO.PUD_UP) GPIO.add_event_detect(cageSettings.entryBBpin, GPIO.BOTH, entryBBCallback) # after exit, update stats writeToLogFile(expSettings.logFP, thisMouse, 'exit') updateH5File(expSettings, cageSettings, mice, stimulator) updateStats(expSettings.statsFP, mice, thisMouse) # after each exit check for a new day if time() > nextDay: # stop lick logging so we dont write to file when it is closed lickDetector.stop_logging() mice.show() writeToLogFile(expSettings.logFP, None, 'SeshEnd') expSettings.logFP.close() expSettings.statsFP.close() makeDayFolderPath(expSettings, cageSettings) makeLogFile(expSettings, cageSettings) simpleLogger.logFP = expSettings.logFP makeQuickStatsFile(expSettings, cageSettings, mice) for i in stimulator: i.nextDay(expSettings.logFP) nextDay += KSECSPERDAY mice.clear() updateH5File(expSettings, cageSettings, mice, stimulator) updateStats(expSettings.statsFP, mice) backup_H5(expSettings, cageSettings) # reinitialize lick detector because it can lock up if too many licks when not logging lickDetector.__init__((0, 1), 26, simpleLogger) lickDetector.start_logging() print('Waiting for a mouse...') else: # check for entry beam break while idling between trials if cageSettings.hasEntryBB == True and time( ) > gTubePanicTime: print( 'Some one has been in the entrance of this tube for too long' ) # explictly turn off pistons, though they should be off headFixer.releaseMouse() BBentryTime = gTubePanicTime - gTubeMaxTime if expSettings.hasTextMsg == True: notifier.notify( 0, (time() - BBentryTime), True ) # we don't have an RFID for this mouse, so use 0 # wait for mouse to leave chamber while time() > gTubePanicTime: sleep(kTIMEOUTmS / 1000) print( 'looks like some one managed to escape the entrance of this tube' ) if expSettings.hasTextMsg == True: notifier.notify(0, (time() - BBentryTime), False) except KeyboardInterrupt: GPIO.output(cageSettings.ledPin, GPIO.LOW) headFixer.releaseMouse() GPIO.output(cageSettings.rewardPin, GPIO.LOW) lickDetector.stop_logging() if cageSettings.hasEntryBB == True: sleep(1) GPIO.remove_event_detect(cageSettings.entryBBpin) print('removed BB event detect') while True: event = input( 'Enter:\nr to return to head fix trials\nq to quit\nv to run valve control\nh for hardware tester\nm for mice inspection\nc for camera configuration\ne for experiment configuration\n:' ) if event == 'r' or event == "R": lickDetector.start_logging() sleep(1) if cageSettings.hasEntryBB == True: sleep(1) print('Restarting entry bb') GPIO.setup(cageSettings.entryBBpin, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.add_event_detect(cageSettings.entryBBpin, GPIO.BOTH, entryBBCallback) break elif event == 'q' or event == 'Q': exit(0) elif event == 'v' or event == "V": valveControl(cageSettings) elif event == 'h' or event == 'H': hardwareTester(cageSettings, tagReader, headFixer, stimulator, expSettings) if cageSettings.contactPolarity == 'RISING': expSettings.contactEdge = GPIO.RISING expSettings.noContactEdge = GPIO.FALLING expSettings.contactState = GPIO.HIGH expSettings.noContactState = GPIO.LOW else: expSettings.contactEdge = GPIO.FALLING expSettings.noContactEdge = GPIO.RISING expSettings.contactState = GPIO.LOW expSettings.noContactState = GPIO.HIGH elif event == 'm' or event == 'M': mice.show() for i, j in enumerate(expSettings.stimulator): print('\t' + str(i) + ': ' + str(j)) inputStr = input( 'Which stimulator-specific inspection method would you like to run?' ) try: stimulator[int(inputStr)].inspect_mice( mice, cageSettings, expSettings) except: print("Stimulator doesn't exist.") updateH5File(expSettings, cageSettings, mice, stimulator) elif event == 'c' or event == 'C': camParams = camera.adjust_config_from_user() elif event == 'e' or event == 'E': modCode = expSettings.edit_from_user() if modCode >= 2: stimulator[modCode - 2] = AHF_Stimulator.get_class( expSettings.stimulator[modCode - 2])( cageSettings, expSettings.stimDict, rewarder, lickDetector, expSettings.logFP, camera) if modCode & 1: for stim in stimulator: stim.change_config(expSettings.stimDict) except Exception as anError: print('AutoHeadFix error:' + str(anError)) raise anError finally: for stim in stimulator: stim.quitting() GPIO.output(cageSettings.ledPin, False) headFixer.releaseMouse() GPIO.output(cageSettings.rewardPin, False) GPIO.cleanup() writeToLogFile(expSettings.logFP, None, 'SeshEnd') expSettings.logFP.close() expSettings.statsFP.close() camera.close() print('AutoHeadFix Stopped')
def config_user_get(cageSet): super(AHF_HeadFixer_PWM_PCA9685).config_user_get(cageSet) userResponse = input( "Servo I2C Address, in Hexadecimal, or enter for default 0x40: ") if userResponse == '': userResponse = '0x40' userResponse = int(userResponse, 16) cageSet.servoAddress = userResponse @staticmethod def config_show(cageSet): rStr = super(AHF_HeadFixer_PWM_PCA9685).config_show(cageSet) rStr += '\n\tServoDriver I2C Address, in Hexadecimal, =' + hex( cageSet.servoAddress) return rStr if __name__ == "__main__": from time import sleep from AHF_CageSet import AHF_CageSet from AHF_HeadFixer import AHF_HeadFixer cageSettings = AHF_CageSet() cageSettings.edit() cageSettings.save() headFixer = AHF_HeadFixer.get_class(cageSettings.headFixer)(cageSettings) headFixer.releaseMouse() sleep(1) headFixer.fixMouse() sleep(1) headFixer.releaseMouse()