Exemple #1
0
class MinutesPerSession(SessionManager):
    minutespersession_version = Ver('0.0.1')
    minutes = None
    hours_between_sessions = None

    def __init__(self, **kwargs):
        super(MinutesPerSession, self).__init__(**kwargs)
        if not kwargs:
            pass
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data]'])
        else:
            pass

    def load_from_dict(self, data):
        self.minutespersession_version = Ver(data['minutespersession_version'])
        self.minutes = data['minutes']
        self.hours_between_sessions = data['hours_between_sessions']

    def save_to_dict(self):
        data = super(MinutesPerSession, self).save_to_dict()
        data[
            'minutespersession_version'] = self.minutespersession_version.__str__(
            )
        data['minutes'] = self.minutes
        data['hours_between_sessions'] = self.hours_between_sessions
        return data

    def __repr__(self):
        return "MinutesPerSession object, %s minutes with %s hrs between sessions" % (
            self.minutes, self.hours_between_sessions)

    def check_schedule(self):
        return NotImplementedError()
Exemple #2
0
class NoTimeOff(SessionManager):
    notimeoff_version = Ver('0.0.1')

    def __init__(self, **kwargs):
        super(NoTimeOff, self).__init__(**kwargs)
        if not kwargs:
            pass
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data]'])
        else:
            pass

    def load_from_dict(self, data):
        self.notimeoff_version = Ver(data['notimeoff_version'])

    def save_to_dict(self):
        data = super(NoTimeOff, self).save_to_dict()
        data['notimeoff_version'] = self.notimeoff_version.__str__()
        return data

    def __repr__(self):
        return "NoTimeOff object"

    def check_schedule(self, **kwargs):
        # Returns keep_doing_trials, secs_remaining_until_state_flip
        keep_doing_trials = True
        secs_remaining_until_state_flip = 0
        return keep_doing_trials, secs_remaining_until_state_flip
Exemple #3
0
class SessionManager(object):
    sessionmgr_version = Ver('0.0.1')
    name = ''

    def __init__(self, **kwargs):
        if not kwargs:
            pass
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data]'])
        else:
            pass

    def load_from_dict(self, data):
        self.sessionmgr_version = Ver(data['sessionmgr_version'])
        self.name = data['name']

    def save_to_dict(self):
        data = dict()
        data['sessionmgr_version'] = self.sessionmgr_version.__str__()
        data['name'] = self.name
        return data

    def __repr__(self):
        return "SessionManager object with name:%s" % (self.name)

    def check_schedule(self):
        return False
Exemple #4
0
class TimeRange(SessionManager):
    timerange_version = Ver('0.0.1')
    time_start = None
    time_stop = None

    def __init__(self, **kwargs):
        super(TimeRange, self).__init__(**kwargs)
        if not kwargs:
            pass
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data]'])
        else:
            pass

    def load_from_dict(self, data):
        self.timerange_version = Ver(data['timerange_version'])
        self.time_start = data['time_start']
        self.time_stop = data['time_stop']

    def save_to_dict(self):
        data = super(TimeRange, self).save_to_dict()
        data['timerange_version'] = self.timerange_version.__str__()
        data['time_start'] = self.time_start
        data['time_stop'] = self.time_stop
        return data

    def __repr__(self):
        return "TimeRange object"

    def check_schedule(self):
        return NotImplementedError()
Exemple #5
0
class Station(object):
    """
        STATION contains all the relevant details and interfaces to run
        trials from a particular station. This is an abstract class.
        Do not instantiate.
        stationID       : numeric ID
        stationPath     : string path to data storage location
        MACAddress      : unique mac address for the processor/ethernet
                          card. string identifier
    """
    _subject = None
    _key_pressed = []
    _sounds = {}
    _stims = {}
    _clocks = {}
    _parallel_port_conn = None
    creation_time = ''
    station_version = Ver('0.0.1')
    station_id = None
    station_name = ''
    station_path = ''
    station_location = None

    def __init__(self, **kwargs):
        if not kwargs:
            pass
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data'])
        else:
            pass

    def load_from_dict(self, data):
        self.station_version = Ver(data['station_version'])
        self.creation_time = datetime.datetime.strptime(
            data['creation_time'], DATETIME_TO_STR)
        self.station_id = data['station_id']
        self.station_name = data['station_name']
        self.station_path = os.path.join(get_base_path(), 'BCoreData',
                                         'StationData', str(self.station_id))
        self.station_location = data['station_location']

        self.mac_address = get_mac_address()
        self.ip_address = get_ip_addr()
        self.port = get_port()
        return self

    def save_to_dict(self):
        data = dict()
        data['station_version'] = self.station_version.__str__()
        data['station_id'] = self.station_id
        data['station_name'] = self.station_name
        data['station_location'] = self.station_location
        data['creation_time'] = datetime.datetime.strftime(
            self.creation_time, DATETIME_TO_STR)
        return data

    def __repr__(self):
        return "Station object with id:%s, location:%s and ip:%s" % (
            self.station_id, self.station_location, self.ip_address)

    def register(self):
        #
        pass

    def _setup_paths(self):
        if not os.path.isdir(self.station_path):
            os.makedirs(self.station_path)

    def do_trials(self, **kwargs):
        raise NotImplementedError('Run doTrials() on a subclass')

    def initialize_sounds(self):
        self._sounds['trial_start_sound'] = psychopy.sound.Sound(440,
                                                                 stereo=0,
                                                                 secs=1.,
                                                                 hamming=True)

        self._sounds['request_sound'] = psychopy.sound.Sound(493.88,
                                                             stereo=0,
                                                             secs=1.,
                                                             hamming=True)
        self._sounds['stim_start_sound'] = psychopy.sound.Sound(493.88,
                                                                stereo=0,
                                                                secs=1.,
                                                                hamming=True)
        self._sounds['go_sound'] = psychopy.sound.Sound(493.88,
                                                        stereo=0,
                                                        secs=1.,
                                                        hamming=True)
        self._sounds['keep_going_sound'] = psychopy.sound.Sound(493.88,
                                                                stereo=0,
                                                                secs=1.,
                                                                hamming=True)
        self._sounds['request_sound'] = psychopy.sound.Sound(493.88,
                                                             stereo=0,
                                                             secs=1.,
                                                             hamming=True)

        self._sounds['correct_sound'] = psychopy.sound.Sound(523.25,
                                                             stereo=0,
                                                             secs=1.,
                                                             hamming=True)
        self._sounds['reward_sound'] = psychopy.sound.Sound(523.25,
                                                            stereo=0,
                                                            secs=1.,
                                                            hamming=True)
        self._sounds['trial_end_sound'] = psychopy.sound.Sound(523.25,
                                                               stereo=0,
                                                               secs=1.,
                                                               hamming=True)

        sampleRate, secs, f_punishment = (44100, 2, [370, 440])
        nSamples = int(secs * sampleRate)
        phase = 2 * np.pi * np.linspace(0.0, 1.0, nSamples)
        val = np.full_like(phase, 0.)
        for f in f_punishment:
            val += np.sin(f * phase)
        val = np.matlib.repmat(val, 2, 1)
        self._sounds['punishment_sound'] = psychopy.sound.Sound(val.T,
                                                                hamming=True)

        val = 0.5 * np.random.randn(1, nSamples)
        val = np.matlib.repmat(val, 2, 1)
        self._sounds['try_something_else'] = psychopy.sound.Sound(val.T,
                                                                  hamming=True)

        self._sounds['trial_end_sound'] = psychopy.sound.Sound(587.33,
                                                               stereo=0,
                                                               secs=1.,
                                                               hamming=True)

    def _rewind_sounds(self, time=0.):
        for sound in self._sounds:
            self._sounds[sound].seek(time)

    def decache(self):
        """
            Remove session specific details. ideal for pickling
        """
        self._key_pressed = None
        self._sounds = None
        self._stims = None
        self._clocks = None
Exemple #6
0
class BServer(object):
    """
        BSERVER  keeps track of all the stations that it commands,
        which subjects are allowed in which station and data storage locations.
            version             : string identifier
            server_id           : string Identifier
            server_data_path    : allowed data storage location
            server_ip           : IPV4 value
            creation_time       : time.time()
            stations            : list of stations
            subjects            : list of subjects
            assignments         : dictionary with keys being subjectID
                                  and values being list of stationIDs
    """
    version = Ver('0.0.1')  # Feb 5, 2014
    server_id = ''
    server_ip = ''
    server_data_path = ''
    creation_time = None
    stations = []
    subjects = []
    assignments = dict()
    server_connection = []
    station_connections = []

    def __init__(self, **kwargs):
        if not kwargs:
            self.creation_time = datetime.datetime.now()
        elif 'data' in kwargs:
            self = self.load_from_dict(kwargs['data'], convert=False)
        else:
            pass

    def load_from_dict(self, data):
        self.version = Ver(data['version'])  # needed
        self.server_id = data['server_id']  # needed
        self.server_data_path = data['server_data_path']  # needed
        self.creation_time = datetime.datetime.strptime(
            data['creation_time'],
            DATETIME_TO_STR)  # Month-dd-YYYY::Hr:Min:Sec
        for sub in data['subjects']:
            self.subjects.append(Subject.load_from_dict(sub))
        for stn in data['stations']:
            self.stations.append(Station.load_from_dict(stn))
        self.assignments = data['assignemnts']
        return self

    def save_to_dict(self):
        data = dict()
        data['version'] = self.version.__str__()
        data['server_id'] = self.server_id
        data['server_data_path'] = self.server_data_path
        data['creation_time'] = datetime.datetime.strftime(
            self.creation_time, DATETIME_TO_STR)

        subjects = []
        for sub in self.subjects:
            subjects.append(sub.save_to_dict())
        data['subjects'] = subjects

        stations = []
        for stn in self.stations:
            stations.append(stn.save_to_dict())
        data['stations'] = stations

        data['assignments'] = self.assignments

    def __repr__(self):
        return "BServer with id:%s, name:%s, created on:%s)" % (
            self.server_id, self.server_name,
            time.strftime("%b-%d-%Y", self.creation_time))

    def run(server, **kwargs):
        # should expose the ZMQ context. and allow connections
        raise NotImplementedError()

    @staticmethod
    def load():
        """
            Alias for server.loadServer
        """
        return BServer.load_server()

    @staticmethod
    def load_server():
        # use standard location for path,
        # make sure to never modify server here:
        dbLoc = os.path.join(get_base_path(), 'BCoreData', 'ServerData',
                             'db.BServer')
        if os.path.isfile(dbLoc):
            with open(dbLoc, 'rb') as f:
                server = json.load(f)
            print('BServer loaded')
        else:
            raise RuntimeError(
                'db.Server not found. Ensure it exists before calling loadServer'
            )
        return server

    def save(self):
        """
            Alias for server.saveServer
        """
        self.save_server()

    def save_server(self):
        srcDir = os.path.join(get_base_path(), 'BCoreData', 'ServerData')
        desDir = os.path.join(get_base_path(), 'BCoreData', 'ServerData',
                              'backupDBs')

        if not os.path.isdir(self.server_data_path):
            # assume that these are never made alone...
            self._setup_paths()

        if os.path.isfile(os.path.join(srcDir, 'db.BServer')):  # old db exists
            print(('Old db.Bserver found. moving to backup'))
            old = BServer()  # standardLoad to old
            des_name = 'db_' + get_time_stamp(old.creation_time) + '.BServer'
            shutil.copyfile(os.path.join(srcDir, 'db.BServer'),
                            os.path.join(desDir, des_name))
            print(('Moved to backup... deleting old copy'))
            os.remove(os.path.join(srcDir, 'db.BServer'))

        # there might be some attributes that need to be deleted
        # delete them here before continuing
        print(('Cleaning and pickling object'))
        cleanedBServer = copy.deepcopy(self)
        cleanedBServer.station_connections = {}
        with open(os.path.join(srcDir, 'db.BServer'), 'wb') as f:
            pickle.dump(cleanedBServer, f)

    def load_backup(self):
        """
            Use this only if you specifically require the deletion of current
            db.BServer and replacement with an older backup. Only the latest
            back up is used.
        """
        desDir = os.path.join(get_base_path(), 'BCoreData', 'ServerData')
        srcDir = os.path.join(get_base_path(), 'BCoreData', 'ServerData',
                              'backupDBs')
        # delete the original database
        os.remove(os.path.join(desDir, 'db.BServer'))
        # find the latest file in the backupDBs
        newestBkup = max(os.listdir(srcDir), key=os.path.getctime)
        shutil.copyfile(
            os.path.join(srcDir, newestBkup),  # source
            os.path.join(desDir, 'db.BServer')  # destination
        )
        # delete the newest backup
        os.remove(os.path.join(srcDir, newestBkup))

    def _setup_paths(server):
        # create 'BServerData'
        os.mkdir(os.path.join(get_base_path(), 'BCoreData'))
        # create 'ServerData','Stations','PermanentTrialRecordStore' in
        # BServerData
        os.mkdir(os.path.join(get_base_path(), 'BCoreData', 'ServerData'))
        os.mkdir(os.path.join(get_base_path(), 'BCoreData', 'StationData'))
        os.mkdir(os.path.join(get_base_path(), 'BCoreData', 'SubjectData'))
        os.mkdir(os.path.join(get_base_path(), 'BCoreData', 'ChangeParams'))
        # create 'replacedDBs' in 'ServerData'
        os.mkdir(
            os.path.join(get_base_path(), 'BCoreData', 'ServerData',
                         'backupDBs'))
        # create 'Full' and 'Compiled' in 'SubjectData'
        os.mkdir(
            os.path.join(get_base_path(), 'BCoreData', 'SubjectData',
                         'SessionRecords'))
        os.mkdir(
            os.path.join(get_base_path(), 'BCoreData', 'SubjectData',
                         'CompiledTrialRecords'))

    def add_station(self, new_station):
        if (new_station.station_id in self.get_station_ids()
                or new_station.station_name in self.get_station_names()):
            raise ValueError('Station IDs and Station Names have to be unique')
        self.stations.append(new_station)
        # now enable station specific data
        self.save()

    def add_subject(self, new_subject):
        if new_subject in self.subjects:
            raise ValueError('Cannot add replica of subjects to BServer')
        self.subjects.append(new_subject)
        self.save()

    def change_assignment(self, subject, new_assignment):
        if subject not in self.subjects:
            raise ValueError('Cannot change assignment on a subject \
            that is not on Bserver')
        if not (any(new_assignment in self.get_station_ids())):
            raise ValueError('Cannot assign subject to non existent stations')
        self.assignments[subject.subject_id] = new_assignment
        self.save()

    def get_station_ids(self):
        station_ids = []
        for station in self.stations:
            station_ids.append(station.station_id)
        return station_ids

    def get_station_names(self):
        station_names = []
        for station in self.stations:
            station_names.append(station.station_name)
        return station_names

    def get_subject_ids(self):
        subject_ids = []
        for subject in self.subjects:
            subject_ids.append(subject.subject_id)
        return subject_ids
Exemple #7
0
class Subject(object):
    """
        SUBJECT contains all relevant details about the subject.
                subject_id              : string identifier
                protocol                : protocol object
                session_manager         : session manager
                creation_time           : time
                iacuc_protocol_id      : string Identifier
        Changes::
            Ver 0.0.2 - Added iacuc_protocol_id to the object
            Ver 0.0.3 - Added  _property_changed and made getters and setters
    """
    _subject_changed = False
    def __init__(self, subject_id, **kwargs):
        """
                Call as follows::
                subject(subjectID='demo')
                subjectID               - MANDATORY
                protocols               - EMPTY
        """
        self.ver = Ver('0.0.3')
        self.subject_id = subject_id
        self._protocol = []
        self._session_manager = []
        self.creation_date = time.time()
        self.iacuc_protocol_id = ''
        if 'reward' in kwargs:
            self._reward = kwargs['reward']
        else:
            self._reward = 0
        if 'timeout' in kwargs:
            self._timeout = kwargs['timeout']
        else:
            self._timeout = 0
        if 'iacuc_protocol_id' in kwargs:
            self.iacuc_protocol_id = kwargs['iacuc_protocol_id']

    def __repr__(self):
        return "Subject with id:%s, rewarded at %s ms and punishment at %s ms" % (self.subject_id, self.reward, self.timeout)

    def _clean(self):
        pass

    def __eq__(self, other):
        # if this method is called, then clearly
        return False
        
    ### getters
    @property
    def protocol(self):
        return self._protocol
        
    @property
    def session_manager(self):
        return self._session_manager
    
    @property
    def reward(self):
        return self._reward
        
    @property
    def timeout(self):
        return self._timeout
    
    ### setters
    @protocol.setter
    def protocol(self, value):
        self._protocol = value
        _subject_changed = True
        
    @session_manager.setter
    def session_manager(self, value):
        self._session_manager = value
        _subject_changed = True
    
    @reward.setter
    def reward(self,value):
        self._reward = value
        _subject_changed = True
        
    @timeout.setter
    def timeout(self, value):
        self._timeout = value
        _subject_changed = True
        
    
    
    
    def add_protocol(self, new_protocol):
        if not self.protocol:
            self.protocol = new_protocol
        else:
            raise ValueError('cannot add new_protocol. protocol is not empty. \
            Maybe you meant replace_protocol()?')

    def add_session_manager(self, new_session_manager):
        if not self.session_manager:
            self.session_manager = new_session_manager
        else:
            raise ValueError('cannot add new_session_manager. session_manager is not empty. \
            Maybe you meant replace_session_manager()?')

    def replace_protocol(self, new_protocol):
        self.protocol = new_protocol

    def replace_session_manager(self, new_session_manager):
        self.session_manager = new_session_manager

    def allowed_genders(self):
        return None

    def allowed_strains(self):
        return None

    def allowed_gene_bkgd(self):
        return None

    def do_trial(self, station, trial_record, compiled_record, quit):
        # Called by station.do_trials()
        if not self.protocol:
            raise ValueError('Protocol Unavailable: cannot run subject without \
            a protocol')

        # new consideration in  protocol and training step
        graduate = False

        # some basic info about the subject
        trial_record['subject_id'] = self.subject_id
        trial_record['subject_version_number'] = self.ver.__str__()

        # figure out the protocol, and the trainingStep details
        trial_record['protocol_name'] = self.protocol.name
        trial_record['protocol_version_number'] = self.protocol.ver.__str__()
        trial_record['current_step'] = self.protocol.current_step
        trial_record['current_step_name'] = self.protocol.step(compiled_record=compiled_record,trial_record=trial_record).name
        trial_record['num_steps'] = self.protocol.num_steps

        current_step = self.protocol.step(compiled_record=compiled_record,trial_record=trial_record)
        trial_record,quit = current_step.do_trial(subject=self, station=station, trial_record=trial_record, compiled_record=compiled_record,quit = quit)
        if trial_record['graduate']:
            trial_record['criterion_met'] = True
            self.protocol.graduate()
        else:
            trial_record['criterion_met'] = False

        return trial_record, quit

    def load_compiled_records(self):
        # location is get_base_directory->BCoreData->CompiledTrialRecords->subject_id.compiled_record
        compiled_file_loc = os.path.join(get_base_directory(),"BCoreData","SubjectData","CompiledTrialRecords")
        files = [i for i in os.listdir(compiled_file_loc) if \
                 os.path.isfile(os.path.join(compiled_file_loc,i)) and self.subject_id in i]
        print(self.subject_id)
        print(files)
        if len(files)>1:
            RuntimeError("SUBJECT:SUBJECT:LOAD_COMPILED_RECORDS:Too many records")
        elif len(files)==1:
            with open(os.path.join(compiled_file_loc,files[0]),"rb") as f:
                cR = pickle.load(f)
        else:
            cR = None

        return cR

    def save_compiled_records(self, cR):
        # location is get_base_directory->BCoreData->SubjectData->CompiledTrialRecords->subject_id.compiled_record
        from BCore import get_base_directory
        import os
        import pickle
        compiled_file_loc = os.path.join(get_base_directory(), "BCoreData", "SubjectData", "CompiledTrialRecords")
        files = [i for i in os.listdir(compiled_file_loc) if \
                 os.path.isfile(os.path.join(compiled_file_loc, i)) and self.subject_id in i]

        if len(files)>1:
            pass
        elif len(files)==1:
            os.remove(os.path.join(compiled_file_loc,files[0]))
        else:
            pass

        tNum = cR["trial_number"][-1]
        sid = self.subject_id
        cR_name = '{0}.1-{1}.compiled_record'.format(sid,tNum)
        with open(os.path.join(compiled_file_loc,cR_name), "wb") as f:
            pickle.dump(cR,f)

    def save_session_records(self,sR):
        # location is get_base_directory->BCoreData->PermanentTrialRecords->subject_id->trialRecord.1-N.session_record
        from BCore import get_base_directory
        import os
        import pickle
        session_file_loc = os.path.join(get_base_directory(), "BCoreData", "SubjectData", "SessionRecords",self.subject_id)
        if not os.path.exists(session_file_loc):
            os.makedirs(session_file_loc)
        tnum_lo = sR[0]["trial_number"]
        tnum_hi = sR[-1]["trial_number"]

        sR_name = "trialRecords.{0}-{1}.session_record".format(tnum_lo,tnum_hi)
        with open(os.path.join(session_file_loc, sR_name), "wb") as f:
            pickle.dump(sR, f)
Exemple #8
0
class StandardVisionBehaviorStation(Station):
    """
        STANDARDVISIONBEHAVIORSTATION(SVBS) defines a subclass of STATION.
        It defines a station with a standard display, a parallel port for i/o
        with standard pin-out settings, sounds settings which can only be
        turned on or off, three valve pins, three sensor pins. Only allows
        stand alone running
        Attributes allowed are:
            station_id       : numeric ID to be sent to STATION
            station_path     : DO NOT SEND - STATION WILL SET IT
            display          : dictionary containing details about the
                               display unit
            soundOn          : True/False
            parallelPort     : dictionary containing details about the parallel
                               port

        For the StandardVisualBehaviorStation, with Rev 2/3 breakout boards
        ("The Bomb"), only certain ports are used and for specific purposes:
            Pin 2            :            Right Reward Valve
            Pin 3            :            Center Reward Valve
            Pin 4            :            Left Reward Valve
            Pin 5            :            LED1
            Pin 6            :            eyePuff
            Pin 7            :            LED2
            Pin 8            :            indexPulse
            Pin 9            :            framePulse
            Pin 10           :            Center Response Sensor
            Pin 12           :            Right Response Sensor
            Pin 13           :            Left Response Sensor
        While, these values are not hard coded here, use these values if you
        want your system to work :)

        Use these defaults unless you know what you are doing
        parallel_port = {}
        parallel_port['right_valve'] = 2
        parallel_port['center_valve'] = 3
        parallel_port['left_valve'] = 4
        parallel_port['valve_pins'] = (2, 3, 4)
        parallel_port['center_port'] = 10
        parallel_port['right_port'] = 12
        parallel_port['left_port'] = 13
        parallel_port['port_pins'] = (12, 10, 13)
        parallel_port['index_pin'] = 8
        parallel_port['frame_pin'] = 9
        parallel_port['led_0'] = 5
        parallel_port['led_1'] = 7
    """
    _window = None
    _session = None
    _server_conn = None

    def __init__(self,
                 sound_on=False,
                 station_id= 0,
                 station_location=(0,0,0),
                 parallel_port='standardVisionBehaviorDefault',
                 parallel_port_address = '/dev/parport0'):
        self.ver = Ver('0.0.1')
        super(StandardVisionBehaviorStation, self).__init__(station_location=station_location)
        self.station_id = station_id
        self.station_name = "Station" + str(station_id)
        self.sound_on = sound_on
        self.parallel_port = parallel_port
        self.parallel_port_address = parallel_port_address
        self.display = self.get_display()
        self.parallel_port = self.get_parport_mappings()

    def __repr__(self):
        return "StandardVisionBehaviorStation object with id:%s, location:%s and ip:%s" %(self.station_id, self.station_location, self.ip_address)

    def get_display(self):
        return StandardDisplay()

    def get_parport_mappings(self):
        if self.parallel_port == 'standardVisionBehaviorDefault':
            print('STANDARDVISIONBEHAVIORSTATION:INITIALIZE_PARALLEL_PORT::setting parallelport to standardVisionBehaviorDefault')
            pPort = {}
            pPort['right_valve'] = 2
            pPort['center_valve'] = 3
            pPort['left_valve'] = 4
            pPort['valve_pins'] = [2, 3, 4]
            pPort['center_port'] = 10
            pPort['right_port'] = 12
            pPort['left_port'] = 13
            pPort['port_pins'] = [13, 10, 12]
            pPort['index_pin'] = 8
            pPort['frame_pin'] = 9
            pPort['trial_pin'] = 6
            pPort['led_0'] = 5
            pPort['led_1'] = 7
            return pPort
        elif self.parallel_port == 'standardHeadfixBehaviorDefault':
            print('STANDARDVISIONBEHAVIORSTATION:INITIALIZE_PARALLEL_PORT::setting parallelport to standardVisionHeadfixDefault')
            pPort = {}
            pPort['reward_valve'] = 3
            pPort['valve_pins'] = [3,]
            pPort['response_port'] = 10
            pPort['running_port'] = 13
            pPort['port_pins'] = [10,]
            pPort['index_pin'] = 8
            pPort['frame_pin'] = 9
            pPort['trial_pin'] = 6
            pPort['led_0'] = 5
            pPort['led_1'] = 7
            return pPort
        else:
            return None # need to write code that checks if allowable

    def initialize(self):
        self.initialize_display(display=self.display)
        self.initialize_sounds()
        self.initialize_parallel_port()
        self.close_all_valves()

    def run(self):
        self.connect_to_server()
        run_trials = False
        while True:
            # look for data from server
            msg = self.get_server_msg()
            quit = False
            if run_trials and ~quit:
                # get info anout session
                self.get_session()

                sub = self._session['subject']
                tR = self._session['trial_record']
                cR = self._session['compiled_record']
                prot = self._session['protocol']
                trial_num = self._session['trial_num']

    def initialize_display(self, display = StandardDisplay()):
        self._window = psychopy.visual.Window(color=(0.,0.,0.), fullscr=True, winType='pyglet', allowGUI=False, units='deg', screen=0, viewScale=None, waitBlanking=True, allowStencil=True,monitor = display)
        self._window.flip()

    def initialize_parallel_port(self):
        self._parallel_port_conn = psychopy.parallel.ParallelPort(address=self.parallel_port_address)
        self.close_all_valves()

    def connect_to_server(self):
        """
            This is a somewhat complicated handshake. Initially, the
            station acts as a server exposing its IP::port to the server.
            Since the server knows this IP::port it can create a client
            connection easily. Upon connection, BServer(currently client)
            sends a connection info for a separate connection(BServer will
            reserve the space for this connection) to the station and the
            station will connect to the BServer now as a client. This way
            new stations can be added to the server without any
            station-side code modification. BServer can dynamically manage
            its resources. Along with threaded TCP server connections on
            the server side, this should provide scalable, TCP communications
            with the server
        """
        self._server_conn = TCPServerConnection(ipaddr=self.ip_address,
            port=self.port)
        self._server_conn.start()
        server_connection_details = self._server_conn.recvData()
        # use server_connection_details to connect to the BServer as a client
        print('STANDARDVISIONBEHAVIORSTATION:CONNECT_TO_SERVER::Closing connection as server...')
        self._server_conn.stop()
        self._server_conn = BehaviorClientConnection(
            ipaddr=server_connection_details['ipaddr'],
            port=server_connection_details['port'])
        print('STANDARDVISIONBEHAVIORSTATION:CONNECT_TO_SERVER::Starting connection as client...')
        self._server_conn.start()

    @property
    def subject(self):
        return self._subject

    @subject.setter
    def subject(self,value):
        self._subject = value

    @property
    def session(self):
        return self._session

    @session.setter
    def session(self,value):
        self._session = value

    def get_ports(self):
        return np.asarray(['left_port','center_port','right_port'])

    @property
    def num_ports(self):
        if self.parallel_port:
            return len(self.parallel_port['port_pins'])
        else:
            return 0

    def add_subject(self, sub):
        self.subject = sub
            #if sub.subject_id in self.get_subjects():
            #    RuntimeError("STATION:STANDARDVISIONBEHAVIORSTATION:ADD_SUBJECT:Subject "+ sub.subject_id + " already in station. Cannot add twice")
            #
            #print("STATION:STANDARDVISIONBEHAVIORSTATION:ADD_SUBJECT: Adding subject_id " + sub.subject_id +" to station_id " + str(self.station_id))
            #self.subjects.append(sub)

    def remove_subject(self,sub):
        self.subject = None
            #if sub.subject_id not in self.get_subjects():
            #    RuntimeError("STATION:STANDARDVISIONBEHAVIORSTATION:ADD_SUBJECT:Subject "+ sub.subject_id + " not in station. Cannot remove.")
            #print("STATION:STANDARDVISIONBEHAVIORSTATION:REMOVE_SUBJECT: Removing subject_id " + sub.subject_id +" from station_id " + str(self.station_id))
            #idx = [i for (i,x) in enumerate(self.get_subjects()) if x==sub.subject_id]
            #self.subjects = self.subjects[:idx[0]]+self.subjects[idx[0]+1:]

    def close_all_valves(self):
        val = list('{0:08b}'.format(self._parallel_port_conn.readData()))
        for valve in self.parallel_port['valve_pins']:
            val[1-valve] = '0'
        self._parallel_port_conn.setData(int(''.join(val),2))

    def read_ports(self):
        out = [False, False, False]
        port_names = ['left_port','center_port','right_port']
        for i,port in enumerate(self.parallel_port['port_pins']):
            out[i] = self._parallel_port_conn.readPin(port)
        active_ports = [x for x,y in zip(port_names,out) if not y]
        return active_ports

    def open_valve(self, valve):
        valve_pin = self.parallel_port[valve]
        self.set_pin_on(valve_pin)

    def close_valve(self, valve):
        valve_pin = self.parallel_port[valve]
        self.set_pin_off(valve_pin)

    def flush_valves(self, dur=1):
        val = list('{0:08b}'.format(self._parallel_port_conn.readData()))
        for valve in self.parallel_port['valve_pins']:
            val[1-valve] = '1'
        self._parallel_port_conn.setData(int(''.join(val),2))

        time.sleep(dur)

        for valve in self.parallel_port['valve_pins']:
            val[1-valve] = '0'
        self._parallel_port_conn.setData(int(''.join(val),2))

    def set_index_pin_on(self):
        index_pin = self.parallel_port['index_pin']
        self.set_pin_on(index_pin)

    def set_index_pin_off(self):
        index_pin = self.parallel_port['index_pin']
        self.set_pin_off(index_pin)

    def set_frame_pin_on(self):
        frame_pin = self.parallel_port['frame_pin']
        self.set_pin_on(frame_pin)

    def set_frame_pin_off(self):
        frame_pin = self.parallel_port['frame_pin']
        self.set_pin_off(frame_pin)

    def set_trial_pin_on(self):
        trial_pin = self.parallel_port['trial_pin']
        self.set_pin_on(trial_pin)

    def set_trial_pin_off(self):
        trial_pin = self.parallel_port['trial_pin']
        self.set_pin_off(trial_pin)

    def set_pin_on(self,pin):
        if pin<2 or pin>9:
            ValueError('Cannot deal with this')
        val = list('{0:08b}'.format(self._parallel_port_conn.readData()))
        val[1-pin] = '1'
        self._parallel_port_conn.setData(int(''.join(val),2))

    def set_pin_off(self,pin):
        if pin<2 or pin>9:
            ValueError('Cannot deal with this')
        val = list('{0:08b}'.format(self._parallel_port_conn.readData()))
        val[1-pin] = '0'
        self._parallel_port_conn.setData(int(''.join(val),2))

    def get_display_size(self):
        pass

    def get_session(self):
        """
            Connect to BServer and request session details to be loaded
        """
        self._session = self._server_conn.client_to_server(self._server_conn.SESSION_REQUESTED)

    def decache(self):
        """
            Remove session specific details. ideal for pickling
        """
        self._window = None
        self._session = None
        self._server_conn = None
        self._parallel_port_conn = None
        self._clocks = None

    def do_trials(self, **kwargs):
        # first step in the running of trials. called directly by station
        # or through the BServer
        if __debug__:
            pass
        self.initialize()
        # get the compiled_records for the animal. Compiled records will contain all the information that will be used in
        # the course of running the experiment. If some stimulus parameter for a given trial is dependent on something in
        # the previous trial, please add it to compiled records
        compiled_record = self.subject.load_compiled_records()
        quit = False

        # session starts here
        session_record = []  # just a list of tRs
        session_number = compiled_record["session_number"][-1] + 1

        # setup the clocks
        self._clocks['session_clock'] = psychopy.core.MonotonicClock()
        self._clocks['trial_clock'] = psychopy.core.Clock()
        session_start_time = psychopy.core.getAbsTime()

        while not quit:
            # it loops in here every trial
            trial_record = {}
            # just assign relevant details here
            trial_record["session_start_time"] = session_start_time
            trial_record["trial_number"] = compiled_record["trial_number"][-1] + 1
            trial_record["session_number"] = session_number
            trial_record["station_id"] = self.station_id
            trial_record["station_version_number"] = self.ver.__str__()
            trial_record["station_name"]= self.station_name
            trial_record["num_ports_in_station"] = self.num_ports
            trial_record["trial_start_time"] = self._clocks['session_clock'].getTime()
            # doTrial - only trial_record will be returned as its type will be changed
            trial_record, quit = self.subject.do_trial(station=self, trial_record=trial_record, compiled_record=compiled_record, quit=quit)

            trial_record["trial_stop_time"] = self._clocks['session_clock'].getTime()
            # update sessionRecord and compiledRecord
            compiled_record = compile_records(compiled_record,trial_record)
            session_record.append(trial_record)

        # save session records
        self.subject.save_session_records(session_record)
        # save compiled records
        self.subject.save_compiled_records(compiled_record)

        self.decache()

    def close_session(self, **kwargs):
        print("STANDARDVISIONBEHAVIORSTATION:CLOSE_SESSION::Closing Session")

    def close_window(self):
        self._window.close()

    def check_manual_quit(self):
        key = psychopy.event.getKeys(keyList=['k','q'])
        if key:
            if not key[0] in self._key_pressed: self._key_pressed.append(key[0])
        if 'k' in self._key_pressed and 'q' in self._key_pressed:
            psychopy.event.clearEvents()
            return True
        else:
            return False

    def read_kb(self):
        key,modifier = event.getKeys(keyList=['1','2','3','k'],modifier=True)
        ports = np.asarray([False,False,False])
        if key:
            if not key[0] in self._key_pressed: self._key_pressed.append(key[0])
        if 'k' in self._key_pressed and '1' in self._key_pressed:
            self._key_pressed.remove('k')
            self._key_pressed.remove('1')
        if 'k' in self._key_pressed and '2' in self._key_pressed:
            ports = np.bitwise_or(ports,[False,True,False])
            psychopy.event.clearEvents()
            print(self._key_pressed)
            self._key_pressed.remove('k')
            self._key_pressed.remove('2')
        if 'k' in self._key_pressed and '3' in self._key_pressed:
            ports = np.bitwise_or(ports,[False,False,True])
            psychopy.event.clearEvents()
            print(self._key_pressed)
            self._key_pressed.remove('k')
            self._key_pressed.remove('3')

        return self.get_ports()[ports]