class stmACAgent: ''' OCS agent class for stimulator AC source ''' def __init__(self, agent, ipaddr=IPADDR_DEFAULT): ''' Parameters ---------- ipaddr : str IP address of AC supply ''' self.active = True self.agent = agent self.log = agent.log self.lock = TimeoutLock() self.take_data = False self.initialized = False self._pcr = PCR500MA(ipaddr) agg_params = {'frame_length': 60} self.agent.register_feed('acsupply', record=True, agg_params=agg_params, buffer_time=1) def init_pcr500(self, session, params=None): '''Initialization of pcr500 AC supply ''' if self.initialized: return True, "Already initialized." with self.lock.acquire_timeout(timeout=0, job='init') as acquired: if not acquired: self.log.warn( "Could not start init because {} is already running". format(self.lock.job)) return False, "Could not acquire lock." try: self._pcr.checkID() except ValueError: pass print("AC supply PCR500 initialized.") self.initialized = True return True, "AC supply PCR500 initialized." def start_acq(self, session, params): '''Starts acquiring data. ''' f_sample = params.get('sampling frequency', 0.1) sleep_time = 1 / f_sample - 0.1 if not self.initialized: self.init_pcr500(session) #with self.lock.acquire_timeout(timeout=0, job='acq') as acquired: # if not acquired: # self.log.warn("Could not start acq because {} is already running".format(self.lock.job)) # return False, "Could not acquire lock." session.set_status('running') self.take_data = True session.data = {"fields": {}} while self.take_data: with self.lock.acquire_timeout(timeout=1, job='acq') as acquired: if not acquired: print( f"Lock could not be acquired because it is held by {self.lock.job}" ) return False current_time = time.time() data = { 'timestamp': current_time, 'block_name': 'acsupply', 'data': {} } voltage = self._pcr.getVoltage() current = self._pcr.getCurrent() power = self._pcr.getPower() if not self.lock.release_and_acquire(timeout=10): print( f"Could not re-acquire lock now held by {self.lock.job}." ) return False data['data']['voltage'] = voltage data['data']['current'] = current data['data']['power'] = power field_dict = { f'acsupply': { 'voltage': voltage, 'current': current, 'power': power } } session.data['fields'].update(field_dict) time.sleep(sleep_time) self.agent.publish_to_feed('acsupply', data) self.agent.feeds['acsupply'].flush_buffer() return True, 'Acquisition exited cleanly.' def stop_acq(self, session, params=None): """ Stops the data acquisiton. """ if self.take_data: self.take_data = False return True, 'requested to stop taking data.' return False, 'acq is not currently running.' def set_values(self, session, params=None): '''A task to set sensor parameters for AC supply volt : float operate AC voltage ''' if params is None: params = {} with self.lock.acquire_timeout(3, job='set_values') as acquired: if not acquired: self.log.warn('Could not start set_values because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' volt = params.get('volt') if not volt is None: self.voltsetting = volt # self._ble2.set_speed(speed) def get_values(self, session, params=None): '''A task to provide configuration information ''' pass def switchPower(self, session, params=None, state=0): '''A task to turn switch, state 0 = off, 1 = on ''' pass def get_settings(self, session, params=None): ''' Get relay states''' if params is None: params = {} with self.lock.acquire_timeout(3, job='get_settings') as acquired: if not acquired: self.log.warn('Could not start get_setting because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' setV = self.Voltage session.data = {'volt': setV} return True, f'Got AC status' def getACstatus(self, session, params=None): with self.lock.acquire_timeout(3, job='get_settings') as acquired: if not acquired: self.log.warn('Could not start get_setting because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' #print(self, session, params) volt = self._pcr._a('MEAS:VOLT:AC?') curr = self._pcr._a('MEAS:CURR:AC?') freq = self._pcr._a('MEAS:FREQ?') power = self._pcr._a('MEAS:POW:AC?') preac = self._pcr._a('MEAS:POW:AC:REAC?') print(volt, curr, freq, power, preac) return True, f'AC {volt}, {curr}, {freq}, {power}, {preac}' def rampVoltage(self, session, params=None): # normal temperature control print(params) voltgoal = params.get('volt', 0) print(voltgoal) if (voltgoal < 0): print("Voltage cannot be negative!") return False, 'Voltage cannot be negative' while (abs(voltgoal - self._pcr.Voltage) > VoltStep): if (self._pcr.Voltage < voltgoal): self._pcr.Voltage = self._pcr.Voltage + VoltStep elif (self._pcr.Voltage > voltgoal): self._pcr.Voltage = self._pcr.Voltage - VoltStep with self.lock.acquire_timeout(timeout=3, job='set_voltage') as acquired: print("Set ", self._pcr.Voltage) self._pcr.setVoltage(self._pcr.Voltage) time.sleep(0.5) print(self._pcr.getCurrent()) time.sleep(WaitTimeStep - 0.5) with self.lock.acquire_timeout(timeout=3, job='set_voltage') as acquired: print("last step to", voltgoal) self._pcr.Voltage = voltgoal print(self, self._pcr.getCurrent()) self._pcr.setVoltage(self._pcr.Voltage) time.sleep(0.5) print(self._pcr.getCurrent()) return True, f'Reached to voltage {voltgoal}' def forceZero(self, session, params=None): #for site work while (self._pcr.Voltage > VoltStep): with self.lock.acquire_timeout(timeout=3, job='set_voltage') as acquired: self._pcr.Voltage = self._pcr.Voltage - VoltStep print("go down to ", self._pcr.Voltage) self._pcr.setVoltage(self._pcr.Voltage) time.sleep(WaitTimeForce) print("set to 0 Volt") with self.lock.acquire_timeout(timeout=3, job='set_voltage') as acquired: self._pcr.Voltage = 0.0 self._pcr.setVoltage(0.0) return True, f'Ramped down to 0 volt.'
class dS378Agent: '''OCS agent class for dS378 ethernet relay ''' def __init__(self, agent, ip=IP_DEFAULT, port=17123): ''' Parameters ---------- ip : string IP address port : int Port number ''' self.active = True self.agent = agent self.log = agent.log self.lock = TimeoutLock() self.take_data = False self._dev = dS378(ip=ip, port=port) self.initialized = False agg_params = {'frame_length': 60} self.agent.register_feed('relay', record=True, agg_params=agg_params, buffer_time=1) def start_acq(self, session, params): '''Starts acquiring data. ''' if params is None: params = {} f_sample = params.get('sampling_frequency', 0.5) sleep_time = 1 / f_sample - 0.1 with self.lock.acquire_timeout(timeout=0, job='acq') as acquired: if not acquired: self.log.warn( f'Could not start acq because {self.lock.job} is already running' ) return False, 'Could not acquire lock.' session.set_status('running') self.take_data = True session.data = {"fields": {}} last_release = time.time() while self.take_data: # Release lock if time.time() - last_release > LOCK_RELEASE_SEC: last_release = time.time() if not self.lock.release_and_acquire( timeout=LOCK_RELEASE_TIMEOUT): print(f'Re-acquire failed: {self.lock.job}') return False, 'Could not re-acquire lock.' # Data acquisition current_time = time.time() data = { 'timestamp': current_time, 'block_name': 'relay', 'data': {} } d_status = self._dev.get_status() relay_list = self._dev.get_relays() data['data']['V_sppl'] = d_status['V_sppl'] data['data']['T_int'] = d_status['T_int'] for i in range(8): data['data'][f'Relay_{i+1}'] = relay_list[i] field_dict = { f'relay': { 'V_sppl': d_status['V_sppl'], 'T_int': d_status['T_int'] } } session.data['fields'].update(field_dict) self.agent.publish_to_feed('relay', data) session.data.update({'timestamp': current_time}) time.sleep(sleep_time) self.agent.feeds['relay'].flush_buffer() return True, 'Acquisition exited cleanly.' def stop_acq(self, session, params=None): """ Stops the data acquisiton. """ if self.take_data: self.take_data = False return True, 'requested to stop taking data.' return False, 'acq is not currently running.' def set_relay(self, session, params=None): '''Turns the relay on/off or pulses it Parameters ---------- relay_number : int relay_number, 1 -- 8 on_off : int or RelayStatus 1: on, 0: off pulse_time : int, 32 bit See document ''' if params is None: params = {} with self.lock.acquire_timeout(3, job='set_values') as acquired: if not acquired: self.log.warn('Could not start set_values because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' if params.get('pulse_time') is None: params['pulse_time'] = 0 self._dev.set_relay(relay_number=params['relay_number'], on_off=params['on_off'], pulse_time=params['pulse_time']) return True, f'Set values for BLE2' def get_relays(self, session, params=None): ''' Get relay states''' if params is None: params = {} with self.lock.acquire_timeout(3, job='get_relays') as acquired: if not acquired: self.log.warn('Could not start get_relays because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' d_status = self._dev.get_relays() session.data = {f'Relay_{i+1}': d_status[i] for i in range(8)} return True, f'Got relay status'
class LATRtXYStageAgent: """ Agent for connecting to the LATRt XY Stages Args: ip_addr: IP address where RPi server is running port: Port the RPi Server is listening on mode: 'acq': Start data acquisition on initialize samp: default sampling frequency in Hz """ def __init__(self, agent, ip_addr, port, mode=None, samp=2): self.ip_addr = ip_addr self.port = port self.xy_stage = None self.initialized = False self.take_data = False self.is_moving = False self.agent = agent self.log = agent.log self.lock = TimeoutLock() if mode == 'acq': self.auto_acq = True else: self.auto_acq = False self.sampling_frequency = float(samp) ### register the position feeds agg_params = { 'frame_length': 10 * 60, #[sec] } self.agent.register_feed('positions', record=True, agg_params=agg_params, buffer_time=0) def init_xy_stage_task(self, session, params=None): """init_xy_stage_task(params=None) Perform first time setup for communivation with XY stages. Args: params (dict): Parameters dictionary for passing parameters to task. """ if params is None: params = {} self.log.debug("Trying to acquire lock") with self.lock.acquire_timeout(timeout=0, job='init') as acquired: # Locking mechanism stops code from proceeding if no lock acquired if not acquired: self.log.warn( "Could not start init because {} is already running". format(self.lock.job)) return False, "Could not acquire lock." # Run the function you want to run self.log.debug("Lock Acquired Connecting to Stages") self.xy_stage = XY_Stage(self.ip_addr, self.port) self.xy_stage.init_stages() print("XY Stages Initialized") # This part is for the record and to allow future calls to proceed, # so does not require the lock self.initialized = True if self.auto_acq: self.agent.start('acq') return True, 'XY Stages Initialized.' def move_x_cm(self, session, params): """ params: dict: { 'distance': float, 'velocity':float < 1.2} """ with self.lock.acquire_timeout(timeout=3, job='move_x_cm') as acquired: if not acquired: self.log.warn( f"Could not start x move because lock held by {self.lock.job}" ) return False self.xy_stage.move_x_cm(params.get('distance', 0), params.get('velocity', 1)) time.sleep(1) while True: ## data acquisition updates the moving field if it is running if not self.take_data: with self.lock.acquire_timeout(timeout=3, job='move_x_cm') as acquired: if not acquired: self.log.warn( f"Could not check because lock held by {self.lock.job}" ) return False, "Could not acquire lock" self.is_moving = self.xy_stage.moving if not self.is_moving: break return True, "X Move Complete" def move_y_cm(self, session, params): """ params: dict: { 'distance': float, 'velocity':float < 1.2} """ with self.lock.acquire_timeout(timeout=3, job='move_y_cm') as acquired: if not acquired: self.log.warn( f"Could not start y move because lock held by {self.lock.job}" ) return False, "could not acquire lock" self.xy_stage.move_y_cm(params.get('distance', 0), params.get('velocity', 1)) time.sleep(1) while True: ## data acquisition updates the moving field if it is running if not self.take_data: with self.lock.acquire_timeout(timeout=3, job='move_y_cm') as acquired: if not acquired: self.log.warn( f"Could not check for move because lock held by {self.lock.job}" ) return False, "could not acquire lock" self.is_moving = self.xy_stage.moving if not self.is_moving: break return True, "Y Move Complete" def set_position(self, session, params): """ params: dict: {'position': (float, float)} """ with self.lock.acquire_timeout(timeout=3, job='set_position') as acquired: if not acquired: self.log.warn( f"Could not set position because lock held by {self.lock.job}" ) return False, "Could not acquire lock" self.xy_stage.position = params['position'] return True, "Position Updated" def start_acq(self, session, params=None): """ params: dict: {'sampling_frequency': float, sampling rate in Hz} """ if params is None: params = {} f_sample = params.get('sampling_frequency', self.sampling_frequency) pm = Pacemaker(f_sample, quantize=True) if not self.initialized or self.xy_stage is None: raise Exception("Connection to XY Stages not initialized") with self.lock.acquire_timeout(timeout=0, job='acq') as acquired: if not acquired: self.log.warn( "Could not start acq because {} is already running".format( self.lock.job)) return False, "Could not acquire lock." self.log.info( f"Starting Data Acquisition for XY Stages at {f_sample} Hz") session.set_status('running') self.take_data = True last_release = time.time() while self.take_data: if time.time() - last_release > 1.: if not self.lock.release_and_acquire(timeout=10): self.log.warn( f"Could not re-acquire lock now held by {self.lock.job}." ) return False, "could not re-acquire lock" last_release = time.time() pm.sleep() data = { 'timestamp': time.time(), 'block_name': 'positions', 'data': {} } pos = self.xy_stage.position self.is_moving = self.xy_stage.moving data['data']['x'] = pos[0] data['data']['y'] = pos[1] self.agent.publish_to_feed('positions', data) return True, 'Acquisition exited cleanly.' def stop_acq(self, session, params=None): """ params: dict: {} """ if self.take_data: self.take_data = False return True, 'requested to stop taking data.' else: return False, 'acq is not currently running.'
class FTSAerotechAgent: """ Agent for connecting to the FTS mirror control Args: ip_addr: IP address of Motion Controller port: Port of Motion Controller mode: 'acq': Start data acquisition on initialize samp: default sampling frequency in Hz """ def __init__(self, agent, ip_addr, port, mode=None, samp=2): self.ip_addr = ip_addr self.port = int(port) self.stage = None self.initialized = False self.take_data = False self.agent = agent self.log = agent.log self.lock = TimeoutLock() if mode == 'acq': self.auto_acq = True else: self.auto_acq = False self.sampling_frequency = float(samp) ### register the position feeds agg_params = { 'frame_length' : 10*60, #[sec] } self.agent.register_feed('position', record = True, agg_params = agg_params, buffer_time = 0) def init_stage_task(self, session, params=None): """init_stage_task(params=None) Perform first time setup for communication with FTS stage. Args: params (dict): Parameters dictionary for passing parameters to task. """ if params is None: params = {} if self.stage is not None and self.initialized: return True, 'Stages already Initialized' self.log.debug("Trying to acquire lock") with self.lock.acquire_timeout(timeout=0, job='init') as acquired: # Locking mechanism stops code from proceeding if no lock acquired if not acquired: self.log.warn("Could not start init because {} is already" \ "running".format(self.lock.job)) return False, "Could not acquire lock." # Run the function you want to run self.log.debug("Lock Acquired Connecting to Stages") try: self.stage = FTSAerotechStage(self.ip_addr, self.port) except Exception as e: self.log.error(f"Error while connecting to FTS: {e}") reactor.callFromThread(reactor.stop) return False, "FTS Stage Initialization Failed" # This part is for the record and to allow future calls to proceed, # so does not require the lock self.initialized = True if self.auto_acq: self.agent.start('acq') return True, 'Stage Initialized.' def home_task(self, session, params=None): """ Home the stage to its negative limit """ with self.lock.acquire_timeout(timeout=3, job='home') as acquired: if not acquired: self.log.warn("Could not start home because lock held by" \ f"{self.lock.job}") return False, "Could not get lock" try: self.stage.home() except Exception as e: self.log.error(f"Homing Failed: {e}") return False, "Homing Failed" return True, "Homing Complete" def move_to(self, session, params=None): """Move to absolute position relative to stage center (in mm) params: {'position':float between -74.8 and 74.8} """ if params is None: return False, "No Position Given" if 'position' not in params: return False, "No Position Given" with self.lock.acquire_timeout(timeout=3, job='move') as acquired: if not acquired: self.log.warn("Could not start move because lock held by" \ f"{self.lock.job}") return False, "Could not get lock" return self.stage.move_to( params.get('position') ) return False, "Move did not complete correctly?" def start_acq(self, session, params=None): """ params: dict: {'sampling_frequency': float, sampling rate in Hz} The most recent position data is stored in session.data in the format:: {"position":{"pos" : mirror position } """ if params is None: params = {} f_sample = params.get('sampling_frequency', self.sampling_frequency) pm = Pacemaker(f_sample, quantize=True) if not self.initialized or self.stage is None: raise Exception("Connection to Stages not initialized") with self.lock.acquire_timeout(timeout=0, job='acq') as acquired: if not acquired: self.log.warn(f"Could not start acq because {self.lock.job} " \ "is already running") return False, "Could not acquire lock." self.log.info("Starting Data Acquisition for FTS Mirror at" \ f"{f_sample} Hz") session.set_status('running') self.take_data = True last_release = time.time() while self.take_data: if time.time()-last_release > 1.: if not self.lock.release_and_acquire(timeout=20): self.log.warn("Could not re-acquire lock now held by" \ f"{self.lock.job}.") return False, "could not re-acquire lock" last_release = time.time() pm.sleep() data = { 'timestamp':time.time(), 'block_name':'position', 'data':{}} success, pos = self.stage.get_position() if not success: self.log.info("stage.get_position call failed") else: data['data']['pos'] = pos self.agent.publish_to_feed('position',data) return True, 'Acquisition exited cleanly.' def stop_acq(self, session, params=None): """ params: dict: {} """ if self.take_data: self.take_data = False return True, 'requested to stop taking data.' else: return False, 'acq is not currently running.'
class BLE2Agent: '''OCS agent class for BLE2 motor driver ''' def __init__(self, agent, port=PORT_DEFAULT): ''' Parameters ---------- port : string Port to connect ''' self.active = True self.agent = agent self.log = agent.log self.lock = TimeoutLock() self.take_data = False self._ble2 = BLE2(port=port) self.initialized = False agg_params = {'frame_length': 60} self.agent.register_feed('motor', record=True, agg_params=agg_params, buffer_time=1) def init_ble2(self, session, params=None): '''Initialization of BLE2 motor driver''' if self.initialized: return True, 'Already initialized' with self.lock.acquire_timeout(0, job='init_ble2') as acquired: if not acquired: self.log.warn('Could not start init because ' '{} is already running'.format(self.lock.job)) return False, 'Could not acquire lock.' session.set_status('starting') self._ble2.connect() session.add_message('BLE2 initialized.') self.initialized = True return True, 'BLE2 module initialized.' def start_acq(self, session, params): '''Starts acquiring data. ''' if params is None: params = {} f_sample = params.get('sampling_frequency', 2.5) sleep_time = 1 / f_sample - 0.1 if not self.initialized: self.agent.start('init_ble2') for _ in range(INIT_TIMEOUT): if self.initialized: break time.sleep(0.1) if not self.initialized: return False, 'Could not initialize..' with self.lock.acquire_timeout(timeout=0, job='acq') as acquired: if not acquired: self.log.warn( f'Could not start acq because {self.lock.job} is already running' ) return False, 'Could not acquire lock.' session.set_status('running') self.take_data = True session.data = {"fields": {}} last_release = time.time() while self.take_data: # Release lock if time.time() - last_release > LOCK_RELEASE_SEC: last_release = time.time() if not self.lock.release_and_acquire( timeout=LOCK_RELEASE_TIMEOUT): print(f'Re-acquire failed: {self.lock.job}') return False, 'Could not re-acquire lock.' # Data acquisition current_time = time.time() data = { 'timestamp': current_time, 'block_name': 'motor', 'data': {} } speed = self._ble2.get_status() data['data']['RPM'] = speed field_dict = {f'motor': {'RPM': speed}} session.data['fields'].update(field_dict) self.agent.publish_to_feed('motor', data) session.data.update({'timestamp': current_time}) time.sleep(sleep_time) self.agent.feeds['motor'].flush_buffer() return True, 'Acquisition exited cleanly.' def stop_acq(self, session, params=None): """ Stops the data acquisiton. """ if self.take_data: self.take_data = False return True, 'requested to stop taking data.' return False, 'acq is not currently running.' def set_values(self, session, params=None): '''A task to set parameters for BLE2 motor driver Parameters ---------- speed : int Motor rotation speed in RPM accl_time : float Acceleration time decl_time : float Deceleration time ''' if params is None: params = {} with self.lock.acquire_timeout(3, job='set_values') as acquired: if not acquired: self.log.warn('Could not start set_values because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' speed = params.get('speed') if not speed is None: self._ble2.set_speed(speed) accl_time = params.get('accl_time') if not accl_time is None: self._ble2.set_accl_time(accl_time, accl=True) decl_time = params.get('decl_time') if not decl_time is None: self._ble2.set_accl_time(decl_time, accl=False) return True, f'Set values for BLE2' def start_rotation(self, session, params=None): '''Start rotation Parameters ---------- forward : bool, default True Move forward if True ''' if params is None: params = {} if not self.take_data: self.agent.start('acq') for _ in range(ACQ_TIMEOUT): if self.take_data: break time.sleep(0.1) if not self.take_data: return False, 'Could not start acq.' with self.lock.acquire_timeout(3, job='set_values') as acquired: if not acquired: self.log.warn('Could not start set_values because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' if not self.take_data: return False, 'acq is not currently running.' forward = params.get('forward') if forward is None: forward = True self._ble2.start(forward=forward) return True, f'BLE2 rotation started.' def stop_rotation(self, session, params=None): '''Stop rotation''' if params is None: params = {} with self.lock.acquire_timeout(3, job='set_values') as acquired: if not acquired: self.log.warn('Could not start set_values because ' f'{self.lock.job} is already running') return False, 'Could not acquire lock.' self._ble2.stop() return True, f'BLE2 rotation stop command was published.'
class RotationAgent: """Agent to control the rotation speed of the CHWP Args: kikusui_ip (str): IP address for the Kikusui power supply kikusui_port (str): Port for the Kikusui power supply pid_ip (str): IP address for the PID controller pid_port (str): Port for the PID controller pid_verbosity (str): Verbosity of PID controller output """ def __init__(self, agent, kikusui_ip, kikusui_port, pid_ip, pid_port, pid_verbosity): self.agent = agent self.log = agent.log self.lock = TimeoutLock() self._initialized = False self.take_data = False self.kikusui_ip = kikusui_ip self.kikusui_port = int(kikusui_port) self.pid_ip = pid_ip self.pid_port = pid_port self._pid_verbosity = pid_verbosity > 0 self.cmd = None # Command object for PSU commanding self.pid = None # PID object for pid controller commanding agg_params = {'frame_length': 60} self.agent.register_feed('hwprotation', record=True, agg_params=agg_params) @ocs_agent.param('auto_acquire', default=False, type=bool) @ocs_agent.param('force', default=False, type=bool) def init_connection(self, session, params): """init_connection(auto_acquire=False, force=False) **Task** - Initialize connection to Kikusui Power Supply and PID Controller. Parameters: auto_acquire (bool, optional): Default is False. Starts data acquisition after initialization if True. force (bool, optional): Force initialization, even if already initialized. Defaults to False. """ if self._initialized and not params['force']: self.log.info("Connection already initialized. Returning...") return True, "Connection already initialized" with self.lock.acquire_timeout(0, job='init_connection') as acquired: if not acquired: self.log.warn( 'Could not run init_connection because {} is already running' .format(self.lock.job)) return False, 'Could not acquire lock' try: pmx = PMX(tcp_ip=self.kikusui_ip, tcp_port=self.kikusui_port, timeout=0.5) self.cmd = Command(pmx) self.log.info('Connected to Kikusui power supply') except ConnectionRefusedError: self.log.error( 'Could not establish connection to Kikusui power supply') reactor.callFromThread(reactor.stop) return False, 'Unable to connect to Kikusui PSU' try: self.pid = pd.PID(pid_ip=self.pid_ip, pid_port=self.pid_port, verb=self._pid_verbosity) self.log.info('Connected to PID controller') except BrokenPipeError: self.log.error( 'Could not establish connection to PID controller') reactor.callFromThread(reactor.stop) return False, 'Unable to connect to PID controller' self._initialized = True # Start 'iv_acq' Process if requested if params['auto_acquire']: self.agent.start('iv_acq') return True, 'Connection to PSU and PID controller established' def tune_stop(self, session, params): """tune_stop() **Task** - Reverse the drive direction of the PID controller and optimize the PID parameters for deceleration. """ with self.lock.acquire_timeout(3, job='tune_stop') as acquired: if not acquired: self.log.warn( 'Could not tune stop because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' self.pid.tune_stop() return True, 'Reversing Direction' def tune_freq(self, session, params): """tune_freq() **Task** - Tune the PID controller setpoint to the rotation frequency and optimize the PID parameters for rotation. """ with self.lock.acquire_timeout(3, job='tune_freq') as acquired: if not acquired: self.log.warn( 'Could not tune freq because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' self.pid.tune_freq() return True, 'Tuning to setpoint' @ocs_agent.param('freq', default=0., check=lambda x: 0. <= x <= 3.0) def declare_freq(self, session, params): """declare_freq(freq=0) **Task** - Store the entered frequency as the PID setpoint when ``tune_freq()`` is next called. Parameters: freq (float): Desired HWP rotation frequency """ with self.lock.acquire_timeout(3, job='declare_freq') as acquired: if not acquired: self.log.warn( 'Could not declare freq because {} is already running'. format(self.lock.job)) return False, 'Could not acquire lock' self.pid.declare_freq(params['freq']) return True, 'Setpoint at {} Hz'.format(params['freq']) @ocs_agent.param('p', default=0.2, type=float, check=lambda x: 0. < x <= 8.) @ocs_agent.param('i', default=63, type=int, check=lambda x: 0 <= x <= 200) @ocs_agent.param('d', default=0., type=float, check=lambda x: 0. <= x < 10.) def set_pid(self, session, params): """set_pid(p=0.2, i=63, d=0.) **Task** - Set the PID parameters. Note these changes are for the current session only and will change whenever the agent container is reloaded. Parameters: p (float): Proportional PID value i (int): Integral PID value d (float): Derivative PID value """ with self.lock.acquire_timeout(3, job='set_pid') as acquired: if not acquired: self.log.warn( 'Could not set pid because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' self.pid.set_pid([params['p'], params['i'], params['d']]) return True, f"Set PID params to p: {params['p']}, i: {params['i']}, d: {params['d']}" def get_freq(self, session, params): """get_freq() **Task** - Return the current HWP frequency as seen by the PID controller. """ with self.lock.acquire_timeout(3, job='get_freq') as acquired: if not acquired: self.log.warn( 'Could not get freq because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' freq = self.pid.get_freq() return True, 'Current frequency = {}'.format(freq) def get_direction(self, session, params): """get_direction() **Task** - Return the current HWP tune direction as seen by the PID controller. """ with self.lock.acquire_timeout(3, job='get_direction') as acquired: if not acquired: self.log.warn( 'Could not get freq because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' direction = self.pid.get_direction() return True, 'Current Direction = {}'.format(['Forward', 'Reverse'][direction]) @ocs_agent.param('direction', type=str, default='0', choices=['0', '1']) def set_direction(self, session, params): """set_direction(direction='0') **Task** - Set the HWP rotation direction. Parameters: direction (str): '0' for forward and '1' for reverse. """ with self.lock.acquire_timeout(3, job='set_direction') as acquired: if not acquired: self.log.warn( 'Could not set direction because {} is already running'. format(self.lock.job)) return False, 'Could not acquire lock' self.pid.set_direction(params['direction']) return True, 'Set direction' @ocs_agent.param('slope', default=1., type=float, check=lambda x: -10. < x < 10.) @ocs_agent.param('offset', default=0.1, type=float, check=lambda x: -10. < x < 10.) def set_scale(self, session, params): """set_scale(slope=1, offset=0.1) **Task** - Set the PID's internal conversion from input voltage to rotation frequency. Parameters: slope (float): Slope of the "rotation frequency vs input voltage" relationship offset (float): y-intercept of the "rotation frequency vs input voltage" relationship """ with self.lock.acquire_timeout(3, job='set_scale') as acquired: if not acquired: self.log.warn( 'Could not set scale because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' self.pid.set_scale(params['slope'], params['offset']) return True, 'Set scale' def set_on(self, session, params): """set_on() **Task** - Turn on the Kikusui drive voltage. """ with self.lock.acquire_timeout(3, job='set_on') as acquired: if not acquired: self.log.warn( 'Could not set on because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) self.cmd.user_input('on') return True, 'Set Kikusui on' def set_off(self, session, params): """set_off() **Task** - Turn off the Kikusui drive voltage. """ with self.lock.acquire_timeout(3, job='set_off') as acquired: if not acquired: self.log.warn( 'Could not set off because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) self.cmd.user_input('off') return True, 'Set Kikusui off' @ocs_agent.param('volt', default=0, type=float, check=lambda x: 0 <= x <= 35) def set_v(self, session, params): """set_v(volt=0) **Task** - Set the Kikusui drive voltage. Parameters: volt (float): Kikusui set voltage """ with self.lock.acquire_timeout(3, job='set_v') as acquired: if not acquired: self.log.warn( 'Could not set v because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) self.cmd.user_input('V {}'.format(params['volt'])) return True, 'Set Kikusui voltage to {} V'.format(params['volt']) @ocs_agent.param('volt', default=32., type=float, check=lambda x: 0. <= x <= 35.) def set_v_lim(self, session, params): """set_v_lim(volt=32) **Task** - Set the Kikusui drive voltage limit. Parameters: volt (float): Kikusui limit voltage """ with self.lock.acquire_timeout(3, job='set_v_lim') as acquired: if not acquired: self.log.warn( 'Could not set v lim because {} is already running'.format( self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) print(params['volt']) self.cmd.user_input('VL {}'.format(params['volt'])) return True, 'Set Kikusui voltage limit to {} V'.format(params['volt']) def use_ext(self, session, params): """use_ext() **Task** - Set the Kikusui to use an external voltage control. Doing so enables PID control. """ with self.lock.acquire_timeout(3, job='use_ext') as acquired: if not acquired: self.log.warn( 'Could not use external voltage because {} is already running' .format(self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) self.cmd.user_input('U') return True, 'Set Kikusui voltage to PID control' def ign_ext(self, session, params): """ign_ext() **Task** - Set the Kiksui to ignore external voltage control. Doing so disables the PID and switches to direct control. """ with self.lock.acquire_timeout(3, job='ign_ext') as acquired: if not acquired: self.log.warn( 'Could not ignore external voltage because {} is already running' .format(self.lock.job)) return False, 'Could not acquire lock' time.sleep(1) self.cmd.user_input('I') return True, 'Set Kikusui voltage to direct control' @ocs_agent.param('test_mode', default=False, type=bool) def iv_acq(self, session, params): """iv_acq(test_mode=False) **Process** - Start Kikusui data acquisition. Parameters: test_mode (bool, optional): Run the Process loop only once. This is meant only for testing. Default is False. Notes: The most recent data collected is stored in the session data in the structure:: >>> response.session['data'] {'kikusui_volt': 0, 'kikusui_curr': 0, 'last_updated': 1649085992.719602} """ with self.lock.acquire_timeout(timeout=0, job='iv_acq') as acquired: if not acquired: self.log.warn( 'Could not start iv acq because {} is already running'. format(self.lock.job)) return False, 'Could not acquire lock' session.set_status('running') last_release = time.time() self.take_data = True while self.take_data: # Relinquish sampling lock occasionally. if time.time() - last_release > 1.: last_release = time.time() if not self.lock.release_and_acquire(timeout=10): self.log.warn(f"Failed to re-acquire sampling lock, " f"currently held by {self.lock.job}.") continue data = { 'timestamp': time.time(), 'block_name': 'HWPKikusui_IV', 'data': {} } v_msg, v_val = self.cmd.user_input('V?') i_msg, i_val = self.cmd.user_input('C?') data['data']['kikusui_volt'] = v_val data['data']['kikusui_curr'] = i_val self.agent.publish_to_feed('hwprotation', data) session.data = { 'kikusui_volt': v_val, 'kikusui_curr': i_val, 'last_updated': time.time() } time.sleep(1) if params['test_mode']: break self.agent.feeds['hwprotation'].flush_buffer() return True, 'Acqusition exited cleanly' def _stop_iv_acq(self, session, params): """ Stop iv_acq process. """ if self.take_data: self.take_data = False return True, 'requested to stop taking data' return False, 'acq is not currently running'