frappy_psi.furnace: special classes PTXgauge and PRtransmitter

move some initialization from cfg file to source code
+ make 'out of calibrated range' and 'sensor break' more generic

Change-Id: I3e92100fdb9c983f82665de9d8e063609cd7af5a
This commit is contained in:
zolliker 2025-04-23 08:28:08 +02:00
parent e0bd84cc3b
commit d681507f94
3 changed files with 62 additions and 26 deletions

View File

@ -4,18 +4,18 @@ Node('fi.psi.ch',
) )
Mod('T_main', Mod('T_main',
'frappy_psi.ionopimax.CurrentInput', 'frappy_psi.furnace.PRtransmitter',
'sample temperature', 'sample temperature',
addr='ai1', addr='ai1',
valuerange=(0, 1372), valuerange=(0, 2300),
value=Param(unit='degC'), value=Param(unit='degC'),
) )
Mod('T_extra', Mod('T_extra',
'frappy_psi.ionopimax.CurrentInput', 'frappy_psi.furnace.PRtransmitter',
'extra temperature', 'extra temperature',
addr='ai2', addr='ai2',
valuerange=(0, 1372), valuerange=(0, 2300),
value=Param(unit='degC'), value=Param(unit='degC'),
) )
@ -28,6 +28,22 @@ Mod('T_wall',
value=Param(unit='degC'), value=Param(unit='degC'),
) )
Mod('T3',
'frappy_psi.furnace.PRtransmitter',
'extra temperature',
addr='ai3',
valuerange=(0, 1372),
value=Param(unit='degC'),
)
Mod('T4',
'frappy_psi.furnace.PRtransmitter',
'extra temperature',
addr='ai4',
valuerange=(0, 1372),
value=Param(unit='degC'),
)
Mod('T', Mod('T',
'frappy_psi.picontrol.PI', 'frappy_psi.picontrol.PI',
'controlled Temperature', 'controlled Temperature',
@ -77,7 +93,7 @@ Mod('flowswitch',
# ) # )
Mod('p', Mod('p',
'frappy_psi.ionopimax.LogVoltageInput', 'frappy_psi.furnace.PKRgauge',
'pressure reading', 'pressure reading',
addr = 'av1', addr = 'av1',
rawrange = (1.82, 8.6), rawrange = (1.82, 8.6),
@ -89,5 +105,5 @@ Mod('vso',
'frappy_psi.ionopimax.VoltagePower', 'frappy_psi.ionopimax.VoltagePower',
'voltage power output', 'voltage power output',
target = 24, target = 24,
export = False, # export = False,
) )

View File

@ -25,6 +25,7 @@ from frappy.core import Module, Writable, Attached, Parameter, FloatRange, Reada
from frappy.mixins import HasControlledBy from frappy.mixins import HasControlledBy
from frappy_psi.picontrol import PImixin from frappy_psi.picontrol import PImixin
from frappy_psi.convergence import HasConvergence from frappy_psi.convergence import HasConvergence
from frappy_psi.ionopimax import CurrentInput, LogVoltageInput
import frappy_psi.tdkpower as tdkpower import frappy_psi.tdkpower as tdkpower
import frappy_psi.bkpower as bkpower import frappy_psi.bkpower as bkpower
@ -99,3 +100,14 @@ class TdkOutput(HasControlledBy, tdkpower.Output):
class BkOutput(HasControlledBy, bkpower.Output): class BkOutput(HasControlledBy, bkpower.Output):
pass pass
class PRtransmitter(CurrentInput):
rawrange = (0.004, 0.02)
extendedrange = (0.0036, 0.021)
class PKRgauge(LogVoltageInput):
rawrange = (1.82, 8.6)
valuerange = (5e-9, 1000)
extendedrange = (0.5, 9.5)

View File

@ -23,21 +23,22 @@
supports also the smaller model iono pi supports also the smaller model iono pi
""" """
from pathlib import Path
from frappy.core import Readable, Writable, Parameter, BoolType, StringType,\
EnumType, FloatRange, Property, TupleOf, ERROR, IDLE
from frappy.errors import ConfigError, OutOfRangeError, ProgrammingError
from math import log from math import log
from pathlib import Path
from frappy.core import Readable, Writable, Parameter, Property, ERROR, IDLE, WARN
from frappy.errors import ConfigError, OutOfRangeError, ProgrammingError
from frappy.datatypes import BoolType, EnumType, FloatRange, NoneOr, StringType, TupleOf
class Base: class Base:
addr = Property('address', StringType()) addr = Property('address', StringType())
_devpath = None _devpath = None
_devclass = None _devclass = None
_status = None _status = IDLE, ''
def initModule(self): def initModule(self):
super().initModule() super().initModule()
self.log.info('initModule %r', self.name)
candidates = list(Path('/sys/class').glob(f'ionopi*/*/{self.addr}')) candidates = list(Path('/sys/class').glob(f'ionopi*/*/{self.addr}'))
if not candidates: if not candidates:
raise ConfigError(f'can not find path for {self.addr}') raise ConfigError(f'can not find path for {self.addr}')
@ -59,10 +60,9 @@ class Base:
f.write(value) f.write(value)
def read_status(self): def read_status(self):
if self._status is None:
return IDLE, ''
return self._status return self._status
class DigitalInput(Base, Readable): class DigitalInput(Base, Readable):
value = Parameter('input state', BoolType()) value = Parameter('input state', BoolType())
true_level = Property('level representing True', EnumType(low=0, high=1), default=1) true_level = Property('level representing True', EnumType(low=0, high=1), default=1)
@ -82,8 +82,7 @@ class DigitalOutput(DigitalInput, Writable):
def read_value(self): def read_value(self):
reply = self.read(self.addr) reply = self.read(self.addr)
try: try:
self._status = None self._status = IDLE, ''
self.read_status()
value = int(reply) value = int(reply)
except ValueError: except ValueError:
if reply == 'S': if reply == 'S':
@ -95,6 +94,7 @@ class DigitalOutput(DigitalInput, Writable):
else: else:
self._status = ERROR, 'fault while open' self._status = ERROR, 'fault while open'
value = 1 value = 1
self.read_status()
return value == self.true_level return value == self.true_level
def write_target(self, value): def write_target(self, value):
@ -105,13 +105,25 @@ class AnalogInput(Base, Readable):
value = Parameter('analog value', FloatRange()) value = Parameter('analog value', FloatRange())
rawrange = Property('raw range (electronic)', TupleOf(FloatRange(),FloatRange())) rawrange = Property('raw range (electronic)', TupleOf(FloatRange(),FloatRange()))
valuerange = Property('value range (physical)', TupleOf(FloatRange(),FloatRange())) valuerange = Property('value range (physical)', TupleOf(FloatRange(),FloatRange()))
extendedrange = Property('range indicating "out of range", but not seansor fault',
NoneOr(TupleOf(FloatRange(), FloatRange())), default=None)
def read_value(self): def read_value(self):
x0, x1 = self.rawrange x0, x1 = self.rawrange
y0, y1 = self.valuerange y0, y1 = self.valuerange
self.x = self.read(self.addr, self.scale) self.x = self.read(self.addr, self.scale)
self.read_status()
if self.status[0] == ERROR:
raise OutOfRangeError('sensor fault')
return y0 + (y1 - y0) * (self.x - x0) / (x1 - x0) return y0 + (y1 - y0) * (self.x - x0) / (x1 - x0)
def read_status(self):
if self.rawrange[0] <= self.x <= self.rawrange[1]:
return IDLE, ''
if self.extendedrange is None or self.extendedrange[0] <= self.x <= self.extendedrange[1]:
return WARN, 'out of range'
return ERROR, 'sensor fault'
class VoltageInput(AnalogInput): class VoltageInput(AnalogInput):
scale = 1e5 scale = 1e5
@ -127,25 +139,20 @@ class LogVoltageInput(VoltageInput):
x0, x1 = self.rawrange x0, x1 = self.rawrange
y0, y1 = self.valuerange y0, y1 = self.valuerange
self.x = self.read(self.addr, self.scale) self.x = self.read(self.addr, self.scale)
a = (x1-x0)/log(y1/y0,10) self.read_status()
if self.status[0] == ERROR:
raise OutOfRangeError('sensor fault')
a = (x1-x0)/log(y1/y0, 10)
return 10**((self.x-x1)/a)*y1 return 10**((self.x-x1)/a)*y1
class CurrentInput(AnalogInput): class CurrentInput(AnalogInput):
scale = 1e6 scale = 1e6
rawrange = (0.004,0.02) rawrange = (0.004, 0.02)
def initModule(self): def initModule(self):
super().initModule() super().initModule()
self.write(f'{self.addr}_mode','U') self.write(f'{self.addr}_mode', 'U')
def read_value(self):
result = super().read_value()
if self.x > 0.021:
self.status = ERROR, 'sensor fault'
raise OutOfRangeError('sensor fault')
self.status = IDLE, ''
return result
class AnalogOutput(AnalogInput, Writable): class AnalogOutput(AnalogInput, Writable):
@ -175,6 +182,7 @@ class VoltagePower(Base, Writable):
def write_target(self, value): def write_target(self, value):
if value: if value:
self.log.info('write vso %r', value)
self.write(self.addr, value, 1000) self.write(self.addr, value, 1000)
self.write(f'{self.addr}_enabled', 1) self.write(f'{self.addr}_enabled', 1)
else: else: