# ***************************************************************************** # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Module authors: # Markus Zolliker # # ***************************************************************************** """drivers for CCU4, the cryostat control unit at SINQ""" import time # the most common Frappy classes can be imported from frappy.core from frappy.core import EnumType, FloatRange, TupleOf, \ HasIO, Parameter, Command, Readable, StringIO, StatusType, \ BUSY, IDLE, ERROR, DISABLED from frappy.states import HasStates, status_code, Retry class CCU4IO(StringIO): """communication with CCU4""" # for completeness: (not needed, as it is the default) end_of_line = '\n' # on connect, we send 'cid' and expect a reply starting with 'CCU4' identification = [('cid', r'CCU4.*')] # inheriting HasIO allows us to use the communicate method for talking with the hardware # Readable as a base class defines the value and status parameters class HeLevel(HasIO, Readable): """He Level channel of CCU4""" # define the communication class to create the IO module ioClass = CCU4IO # define or alter the parameters # as Readable.value exists already, we give only the modified property 'unit' value = Parameter(unit='%') empty_length = Parameter('warm length when empty', FloatRange(0, 2000, unit='mm'), readonly=False) full_length = Parameter('warm length when full', FloatRange(0, 2000, unit='mm'), readonly=False) sample_rate = Parameter('sample rate', EnumType(slow=0, fast=1), readonly=False) status = Parameter(datatype=StatusType(Readable, 'DISABLED')) # conversion of the code from the CCU4 parameter 'hsf' STATUS_MAP = { 0: (IDLE, 'sensor ok'), 1: (ERROR, 'sensor warm'), 2: (ERROR, 'no sensor'), 3: (ERROR, 'timeout'), 4: (ERROR, 'not yet read'), 5: (DISABLED, 'disabled'), } def query(self, cmd): """send a query and get the response :param cmd: the name of the parameter to query or '= self.value: self.value -= delta / (3600 * self.fill_hours_range[1]) elif self.raw < self.value: self.value -= delta / (3600 * self.fill_hours_range[0]) else: self.value = self.raw if self.value < self.fill_level: self.query('hcd=1 hf=1') state.fillstart = state.now return self.precooling self.query('hcd=1 hf=1') return Retry @status_code(BUSY) def precooling(self, state): delta = state.delta(1) if self.raw > self.value: self.value += delta / (60 * self.fill_minutes_range[0]) elif self.raw < self.value: self.value -= delta / (60 * self.fill_minutes_range[0]) else: self.value = self.raw if self.value > self.full_level: self.query('hcd=0 hf=0') return self.watching self.query('hcd=1 hf=1') if state.now > state.fillstart + self.fill_delay * 60: return self.filling return Retry @status_code(BUSY) def filling(self, state): delta = state.delta(1) if self.raw > self.value: self.value += delta / (60 * self.fill_minutes_range[0]) elif self.raw < self.value: self.value += delta / (60 * self.fill_minutes_range[1]) else: self.value = self.raw if self.value > self.full_level: self.query('hcd=0 hf=0') return self.watching self.query('hcd=1 hf=1') return Retry @Command() def fill(self): self.start_machine(self.precooling, fillstart=time.time()) self.query('hcd=1 hf=1') @Command() def stop(self): pass class N2Sensor(HasIO, Readable): # conversion of the code from the CCU4 parameter 'ns' STATUS_MAP = { 0: (IDLE, 'sensor ok'), 1: (ERROR, 'no sensor'), 2: (ERROR, 'short circuit'), 3: (ERROR, 'upside down'), 4: (ERROR, 'sensor warm'), 5: (ERROR, 'empty'), }