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()
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
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
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()
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
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
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)
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]