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
Exemple #3
0
    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)
Exemple #6
0
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()