Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
a7fd90cd6d |
@ -1,6 +1,6 @@
|
|||||||
Node('flowsas.psi.ch',
|
Node('flowsas.psi.ch',
|
||||||
'flowsas test motors',
|
'flowsas test motors',
|
||||||
'tcp://5000',
|
'tcp://3000',
|
||||||
)
|
)
|
||||||
|
|
||||||
#Mod('mot_io',
|
#Mod('mot_io',
|
||||||
@ -14,7 +14,7 @@ Node('flowsas.psi.ch',
|
|||||||
# 'horizontal axis',
|
# 'horizontal axis',
|
||||||
# axis = 'X',
|
# axis = 'X',
|
||||||
# io = 'mot_io',
|
# io = 'mot_io',
|
||||||
# encoder_mode= 'NO',
|
# encoder_mode = 'NO',
|
||||||
# )
|
# )
|
||||||
|
|
||||||
#Mod('vmot',
|
#Mod('vmot',
|
||||||
@ -28,7 +28,7 @@ Node('flowsas.psi.ch',
|
|||||||
Mod('syr_io',
|
Mod('syr_io',
|
||||||
'frappy_psi.cetoni_pump.LabCannBus',
|
'frappy_psi.cetoni_pump.LabCannBus',
|
||||||
'Module for bus',
|
'Module for bus',
|
||||||
deviceconfig = "/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/dual_pumps",
|
deviceconfig = "/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/conti_flow",
|
||||||
)
|
)
|
||||||
|
|
||||||
Mod('syr1',
|
Mod('syr1',
|
||||||
@ -37,7 +37,7 @@ Mod('syr1',
|
|||||||
io='syr_io',
|
io='syr_io',
|
||||||
pump_name = "Nemesys_S_1_Pump",
|
pump_name = "Nemesys_S_1_Pump",
|
||||||
valve_name = "Nemesys_S_1_Valve",
|
valve_name = "Nemesys_S_1_Valve",
|
||||||
inner_diameter_set = 10,
|
inner_diameter_set = 14.5673,
|
||||||
piston_stroke_set = 60,
|
piston_stroke_set = 60,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,6 +47,14 @@ Mod('syr2',
|
|||||||
io='syr_io',
|
io='syr_io',
|
||||||
pump_name = "Nemesys_S_2_Pump",
|
pump_name = "Nemesys_S_2_Pump",
|
||||||
valve_name = "Nemesys_S_2_Valve",
|
valve_name = "Nemesys_S_2_Valve",
|
||||||
inner_diameter_set = 1,
|
inner_diameter_set = 14.5673,
|
||||||
piston_stroke_set = 60,
|
piston_stroke_set = 60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Mod('contiflow',
|
||||||
|
'frappy_psi.cetoni_pump.ContiFlowPump',
|
||||||
|
'Continuous flow pump',
|
||||||
|
io='syr_io',
|
||||||
|
inner_diameter_set = 14.5673,
|
||||||
|
piston_stroke_set = 60,
|
||||||
|
)
|
12
cfg/peristaltic_pump_cfg.py
Normal file
12
cfg/peristaltic_pump_cfg.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Node('flowsas.psi.ch',
|
||||||
|
'peristaltic pump',
|
||||||
|
'tcp://3000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('peripump',
|
||||||
|
'frappy_psi.gilsonpump.PeristalticPump',
|
||||||
|
'Peristaltic pump',
|
||||||
|
addr_AO = 'ao1',
|
||||||
|
addr_dir_relay = 'o1',
|
||||||
|
addr_run_relay = 'o2',
|
||||||
|
)
|
13
cfg/pressureTest_cfg.py
Normal file
13
cfg/pressureTest_cfg.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Node('vf.psi.ch',
|
||||||
|
'small vacuum furnace',
|
||||||
|
'tcp://5000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('p',
|
||||||
|
'frappy_psi.ionopimax.VoltageInput',
|
||||||
|
'Vacuum pressure',
|
||||||
|
addr = 'av2',
|
||||||
|
rawrange = (0, 10),
|
||||||
|
valuerange = (0, 10),
|
||||||
|
value = Param(unit='V'),
|
||||||
|
)
|
11
cfg/rheotrigger_cfg.py
Normal file
11
cfg/rheotrigger_cfg.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Node('flowsas.psi.ch',
|
||||||
|
'rheometer triggering',
|
||||||
|
'tcp://3000',
|
||||||
|
)
|
||||||
|
|
||||||
|
Mod('rheo',
|
||||||
|
'frappy_psi.rheo_trigger.RheoTrigger',
|
||||||
|
'Trigger for the rheometer',
|
||||||
|
addr='dt1',
|
||||||
|
doBeep = False,
|
||||||
|
)
|
@ -4,13 +4,15 @@ if libpath not in sys.path:
|
|||||||
sys.path.append(libpath)
|
sys.path.append(libpath)
|
||||||
|
|
||||||
from frappy.core import Drivable, Readable, StringIO, HasIO, FloatRange, IntRange, StringType, BoolType, EnumType, \
|
from frappy.core import Drivable, Readable, StringIO, HasIO, FloatRange, IntRange, StringType, BoolType, EnumType, \
|
||||||
Parameter, Property, PersistentParam, Command, IDLE, BUSY, ERROR, Attached
|
Parameter, Property, PersistentParam, Command, IDLE, BUSY, ERROR, WARN, Attached, Module
|
||||||
from qmixsdk import qmixbus
|
from qmixsdk import qmixbus
|
||||||
from qmixsdk import qmixpump
|
from qmixsdk import qmixpump
|
||||||
from qmixsdk import qmixvalve
|
from qmixsdk import qmixvalve
|
||||||
|
from qmixsdk.qmixpump import ContiFlowProperty, ContiFlowSwitchingMode
|
||||||
from qmixsdk.qmixbus import UnitPrefix, TimeUnit
|
from qmixsdk.qmixbus import UnitPrefix, TimeUnit
|
||||||
|
import time
|
||||||
|
|
||||||
class LabCannBus(Readable):
|
class LabCannBus(Module):
|
||||||
deviceconfig = Property('config files', StringType(),default="/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/dual_pumps")
|
deviceconfig = Property('config files', StringType(),default="/home/l_samenv/frappy/cetoniSDK/CETONI_SDK_Raspi_64bit_v20220627/config/dual_pumps")
|
||||||
|
|
||||||
def earlyInit(self):
|
def earlyInit(self):
|
||||||
@ -22,11 +24,15 @@ class LabCannBus(Readable):
|
|||||||
super().initModule()
|
super().initModule()
|
||||||
self.bus.start()
|
self.bus.start()
|
||||||
|
|
||||||
|
with open('/sys/class/ionopimax/buzzer/beep', 'w') as f :
|
||||||
|
f.write('200 50 3')
|
||||||
|
|
||||||
def shutdownModule(self):
|
def shutdownModule(self):
|
||||||
"""Not so gracefully close the connection"""
|
"""Close the connection"""
|
||||||
self.bus.stop()
|
self.bus.stop()
|
||||||
self.bus.close()
|
self.bus.close()
|
||||||
|
|
||||||
|
|
||||||
class SyringePump(Drivable):
|
class SyringePump(Drivable):
|
||||||
io = Attached()
|
io = Attached()
|
||||||
pump_name = Property('name of pump', StringType(),default="Nemesys_S_1_Pump")
|
pump_name = Property('name of pump', StringType(),default="Nemesys_S_1_Pump")
|
||||||
@ -35,18 +41,26 @@ class SyringePump(Drivable):
|
|||||||
inner_diameter_set = Property('inner diameter', FloatRange(), default=1)
|
inner_diameter_set = Property('inner diameter', FloatRange(), default=1)
|
||||||
piston_stroke_set = Property('piston stroke', FloatRange(), default=60)
|
piston_stroke_set = Property('piston stroke', FloatRange(), default=60)
|
||||||
|
|
||||||
value = PersistentParam('volume', FloatRange(unit='mL'))
|
value = Parameter('volume', FloatRange(unit='uL'))
|
||||||
status = PersistentParam()
|
status = Parameter()
|
||||||
|
|
||||||
|
max_flow_rate = Parameter('max flow rate', FloatRange(0,100000, unit='uL/s',), readonly=True)
|
||||||
|
max_volume = Parameter('max volume', FloatRange(0,100000, unit='uL',), readonly=True)
|
||||||
|
|
||||||
max_flow_rate = Parameter('max flow rate', FloatRange(0,100000, unit='mL/min',), readonly=True)
|
target_flow_rate = Parameter('target flow rate', FloatRange(unit='uL/s'), readonly=False)
|
||||||
max_volume = Parameter('max volume', FloatRange(0,100000, unit='mL',), readonly=True)
|
real_flow_rate = Parameter('actual flow rate', FloatRange(unit='uL/s'), readonly=True)
|
||||||
target_flow_rate = Parameter('target flow rate', FloatRange(unit='mL/min'), readonly=False)
|
|
||||||
real_flow_rate = Parameter('actual flow rate', FloatRange(unit='mL/min'), readonly=True)
|
target = Parameter('target volume', FloatRange(unit='uL'), readonly=False)
|
||||||
target = Parameter('target volume', FloatRange(unit='mL'), readonly=False)
|
|
||||||
|
|
||||||
no_of_valve_pos = Property('number of valve positions', IntRange(0,10), default=1)
|
no_of_valve_pos = Property('number of valve positions', IntRange(0,10), default=1)
|
||||||
valve_pos = Parameter('valve position', EnumType('valve', CLOSED=0, APP=1, RES=2, OPEN=3), readonly=False)
|
valve_pos = Parameter('valve position', EnumType('valve', CLOSED=0, APP=1, RES=2, OPEN=3), readonly=False)
|
||||||
|
|
||||||
|
force = Parameter('syringe force', FloatRange(unit='kN'), readonly=True)
|
||||||
|
max_force = Parameter('max device force', FloatRange(unit='kN'), readonly=True)
|
||||||
|
force_limit = Parameter('user force limit', FloatRange(unit='kN'), readonly=False)
|
||||||
|
|
||||||
|
_resolving_force_overload = False
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
super().initModule()
|
super().initModule()
|
||||||
|
|
||||||
@ -66,32 +80,41 @@ class SyringePump(Drivable):
|
|||||||
|
|
||||||
self.pump.set_syringe_param(self.inner_diameter_set, self.piston_stroke_set)
|
self.pump.set_syringe_param(self.inner_diameter_set, self.piston_stroke_set)
|
||||||
|
|
||||||
self.pump.set_volume_unit(qmixpump.UnitPrefix.milli, qmixpump.VolumeUnit.litres)
|
self.pump.set_volume_unit(qmixpump.UnitPrefix.micro, qmixpump.VolumeUnit.litres)
|
||||||
|
|
||||||
self.pump.set_flow_unit(qmixpump.UnitPrefix.milli, qmixpump.VolumeUnit.litres, qmixpump.TimeUnit.per_minute)
|
self.pump.set_flow_unit(qmixpump.UnitPrefix.micro, qmixpump.VolumeUnit.litres, qmixpump.TimeUnit.per_second)
|
||||||
|
|
||||||
self.max_flow_rate = self.pump.get_flow_rate_max()
|
self.max_flow_rate = round(self.pump.get_flow_rate_max(),2)
|
||||||
self.max_volume = self.pump.get_volume_max()
|
self.max_volume = round(self.pump.get_volume_max(),2)
|
||||||
self.no_of_valve_pos = self.valve.number_of_valve_positions()
|
|
||||||
self.valve_pos = self.valve.actual_valve_position()
|
self.valve_pos = self.valve.actual_valve_position()
|
||||||
|
|
||||||
self.target_flow_rate = self.max_flow_rate * 0.5
|
self.target_flow_rate = round(self.max_flow_rate * 0.5,2)
|
||||||
self.target = self.pump.get_fill_level()
|
self.target = max(0, round(self.pump.get_fill_level(),2))
|
||||||
|
|
||||||
|
self.pump.enable_force_monitoring(True)
|
||||||
|
self.max_force = self.pump.get_max_device_force()
|
||||||
|
self.force_limit = self.max_force
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.pump.get_fill_level()
|
return round(self.pump.get_fill_level(),2)
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.pump.set_fill_level(target, self.target_flow_rate)
|
if self.read_valve_pos() == 0 :
|
||||||
self.status = BUSY, 'Target changed'
|
self.status = ERROR, 'Cannot pump if valve is closed'
|
||||||
return target
|
self.log.warn('Cannot pump if valve is closed')
|
||||||
|
return target
|
||||||
|
else:
|
||||||
|
self.pump.set_fill_level(target, self.target_flow_rate)
|
||||||
|
self.status = BUSY, 'Target changed'
|
||||||
|
self.log.info(f'Started pumping at {self.target_flow_rate} ul/s')
|
||||||
|
return target
|
||||||
|
|
||||||
def write_target_flow_rate(self, rate):
|
def write_target_flow_rate(self, rate):
|
||||||
self.pump.target_flow_rate = rate
|
self.target_flow_rate = rate
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
def read_real_flow_rate(self):
|
def read_real_flow_rate(self):
|
||||||
return self.pump.get_flow_is()
|
return round(self.pump.get_flow_is(),2)
|
||||||
|
|
||||||
def read_valve_pos(self):
|
def read_valve_pos(self):
|
||||||
return self.valve.actual_valve_position()
|
return self.valve.actual_valve_position()
|
||||||
@ -100,11 +123,165 @@ class SyringePump(Drivable):
|
|||||||
self.valve.switch_valve_to_position(target_pos)
|
self.valve.switch_valve_to_position(target_pos)
|
||||||
return target_pos
|
return target_pos
|
||||||
|
|
||||||
|
def read_force(self):
|
||||||
|
return round(self.pump.read_force_sensor(),3)
|
||||||
|
|
||||||
|
def read_force_limit(self):
|
||||||
|
return self.pump.get_force_limit()
|
||||||
|
|
||||||
|
def write_force_limit(self, limit):
|
||||||
|
self.pump.write_force_limit(limit)
|
||||||
|
return limit
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
fault_state = self.pump.is_in_fault_state()
|
fault_state = self.pump.is_in_fault_state()
|
||||||
pumping = self.pump.is_pumping()
|
pumping = self.pump.is_pumping()
|
||||||
|
pump_enabled = self.pump.is_enabled()
|
||||||
|
safety_stop_active = self.pump.is_force_safety_stop_active()
|
||||||
|
|
||||||
if fault_state == True:
|
if fault_state == True:
|
||||||
return ERROR, 'Pump in fault state'
|
return ERROR, 'Pump in fault state'
|
||||||
|
elif self._resolving_force_overload :
|
||||||
|
return BUSY, 'Resolving force overload'
|
||||||
|
elif safety_stop_active:
|
||||||
|
return ERROR, 'Pressure safety stop'
|
||||||
|
elif not pump_enabled:
|
||||||
|
return ERROR, 'Pump not enabled'
|
||||||
|
elif pumping == True:
|
||||||
|
return BUSY, f'Pumping {self.real_flow_rate} ul/s'
|
||||||
|
elif self.read_valve_pos() == 0:
|
||||||
|
return IDLE, 'Valve closed'
|
||||||
|
else:
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def stop(self):
|
||||||
|
self.pump.stop_pumping()
|
||||||
|
self.target = self.pump.get_fill_level()
|
||||||
|
self.status = BUSY, 'Stopping'
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def clear_errors(self):
|
||||||
|
"""Clear fault state and enable pump"""
|
||||||
|
if self.pump.is_in_fault_state():
|
||||||
|
self.pump.clear_fault()
|
||||||
|
self.log.info('Cleared faults')
|
||||||
|
|
||||||
|
if not self.pump.is_enabled():
|
||||||
|
self.pump.enable(True)
|
||||||
|
self.log.info('Pump was disabled, re-enabling')
|
||||||
|
|
||||||
|
self.target = max(0,round(self.value,2))
|
||||||
|
self.status = IDLE, ''
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def resolve_force_overload(self):
|
||||||
|
"""Resolve a force overload situation"""
|
||||||
|
if not self.pump.is_force_safety_stop_active():
|
||||||
|
self.status = ERROR, 'No force overload detected'
|
||||||
|
self.log.warn('No force overload to be resolved')
|
||||||
|
return
|
||||||
|
|
||||||
|
self._resolving_force_overload = True
|
||||||
|
self.status = BUSY, 'Resolving force overload'
|
||||||
|
self.pump.enable_force_monitoring(False)
|
||||||
|
|
||||||
|
flow = 0 - self.pump.get_flow_rate_max() / 100
|
||||||
|
self.pump.generate_flow(flow)
|
||||||
|
|
||||||
|
safety_stop_active = False
|
||||||
|
while not safety_stop_active:
|
||||||
|
time.sleep(0.1)
|
||||||
|
safety_stop_active = self.pump.is_force_safety_stop_active()
|
||||||
|
self.pump.stop_pumping()
|
||||||
|
self.pump.enable_force_monitoring(True)
|
||||||
|
|
||||||
|
time.sleep(0.3)
|
||||||
|
self._resolving_force_overload = False
|
||||||
|
self.status = self.read_status()
|
||||||
|
|
||||||
|
class ContiFlowPump(Drivable):
|
||||||
|
io = Attached()
|
||||||
|
|
||||||
|
inner_diameter_set = Property('inner diameter', FloatRange(), default=1)
|
||||||
|
piston_stroke_set = Property('piston stroke', FloatRange(), default=60)
|
||||||
|
|
||||||
|
crossflow_seconds = Property('crossflow duration', FloatRange(unit='s'),default=2)
|
||||||
|
|
||||||
|
value = PersistentParam('flow rate', FloatRange(unit='uL/s'))
|
||||||
|
status = PersistentParam()
|
||||||
|
|
||||||
|
max_refill_flow = Parameter('max refill flow', FloatRange(unit='uL/s'), readonly=True)
|
||||||
|
refill_flow = Parameter('refill flow', FloatRange(unit='uL/s'), readonly=False)
|
||||||
|
|
||||||
|
max_flow_rate = Parameter('max flow rate', FloatRange(0,100000, unit='uL/s',), readonly=True)
|
||||||
|
target = Parameter('target flow rate', FloatRange(unit='uL/s'), readonly=False)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
|
||||||
|
self.pump = qmixpump.ContiFlowPump()
|
||||||
|
self.pump.lookup_by_name("ContiFlowPump_1")
|
||||||
|
|
||||||
|
def initialReads(self):
|
||||||
|
if self.pump.is_in_fault_state():
|
||||||
|
self.pump.clear_fault()
|
||||||
|
|
||||||
|
if not self.pump.is_enabled():
|
||||||
|
self.pump.enable(True)
|
||||||
|
|
||||||
|
self.syringe_pump1 = self.pump.get_syringe_pump(0)
|
||||||
|
self.syringe_pump1.set_syringe_param(self.inner_diameter_set, self.piston_stroke_set)
|
||||||
|
self.syringe_pump2 = self.pump.get_syringe_pump(1)
|
||||||
|
self.syringe_pump2.set_syringe_param(self.inner_diameter_set, self.piston_stroke_set)
|
||||||
|
self.pump.set_volume_unit(qmixpump.UnitPrefix.micro, qmixpump.VolumeUnit.litres)
|
||||||
|
self.pump.set_flow_unit(qmixpump.UnitPrefix.micro, qmixpump.VolumeUnit.litres, qmixpump.TimeUnit.per_second)
|
||||||
|
|
||||||
|
self.pump.set_device_property(ContiFlowProperty.SWITCHING_MODE, ContiFlowSwitchingMode.CROSS_FLOW)
|
||||||
|
self.max_refill_flow = self.pump.get_device_property(ContiFlowProperty.MAX_REFILL_FLOW)
|
||||||
|
self.pump.set_device_property(ContiFlowProperty.REFILL_FLOW, self.max_refill_flow / 2.0)
|
||||||
|
self.pump.set_device_property(ContiFlowProperty.CROSSFLOW_DURATION_S, self.crossflow_seconds)
|
||||||
|
self.pump.set_device_property(ContiFlowProperty.OVERLAP_DURATION_S, 0)
|
||||||
|
|
||||||
|
self.max_flow_rate = self.pump.get_flow_rate_max()
|
||||||
|
self.target = 0
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
return round(self.pump.get_flow_is(),3)
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
if target <= 0:
|
||||||
|
self.pump.stop_pumping()
|
||||||
|
self.status = self.read_status()
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
self.pump.generate_flow(target)
|
||||||
|
self.status = BUSY, 'Target changed'
|
||||||
|
return target
|
||||||
|
|
||||||
|
def read_refill_flow(self):
|
||||||
|
return round(self.pump.get_device_property(ContiFlowProperty.REFILL_FLOW),3)
|
||||||
|
|
||||||
|
def write_refill_flow(self, refill_flow):
|
||||||
|
self.pump.set_device_property(ContiFlowProperty.REFILL_FLOW, refill_flow)
|
||||||
|
self.max_flow_rate = self.pump.get_flow_rate_max()
|
||||||
|
return refill_flow
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
fault_state = self.pump.is_in_fault_state()
|
||||||
|
pumping = self.pump.is_pumping()
|
||||||
|
pump_enabled = self.pump.is_enabled()
|
||||||
|
pump_initialised = self.pump.is_initialized()
|
||||||
|
pump_initialising = self.pump.is_initializing()
|
||||||
|
|
||||||
|
if fault_state == True:
|
||||||
|
return ERROR, 'Pump in fault state'
|
||||||
|
elif not pump_enabled:
|
||||||
|
return ERROR, 'Pump not enabled'
|
||||||
|
elif not pump_initialised:
|
||||||
|
return WARN, 'Pump not initialised'
|
||||||
|
elif pump_initialising:
|
||||||
|
return BUSY, 'Pump initialising'
|
||||||
elif pumping == True:
|
elif pumping == True:
|
||||||
return BUSY, 'Pumping'
|
return BUSY, 'Pumping'
|
||||||
else:
|
else:
|
||||||
@ -113,4 +290,24 @@ class SyringePump(Drivable):
|
|||||||
@Command
|
@Command
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.pump.stop_pumping()
|
self.pump.stop_pumping()
|
||||||
self.target = self.pump.get_fill_level()
|
self.target = 0
|
||||||
|
self.status = BUSY, 'Stopping'
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def clear_errors(self):
|
||||||
|
"""Clear fault state and enable pump"""
|
||||||
|
if self.pump.is_in_fault_state():
|
||||||
|
self.pump.clear_fault()
|
||||||
|
self.log.info('Cleared faults')
|
||||||
|
|
||||||
|
if not self.pump.is_enabled():
|
||||||
|
self.pump.enable(True)
|
||||||
|
self.log.info('Pump was disabled, re-enabling')
|
||||||
|
|
||||||
|
self.target = 0
|
||||||
|
self.status = IDLE, ''
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def initialise(self):
|
||||||
|
"""Initialise the ConfiFlow pump"""
|
||||||
|
self.pump.initialize()
|
104
frappy_psi/gilsonpump.py
Normal file
104
frappy_psi/gilsonpump.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# Author: Wouter Gruenewald<wouter.gruenewald@psi.ch>
|
||||||
|
|
||||||
|
from frappy.core import StringType, BoolType, EnumType, FloatRange, Parameter, Property, PersistentParam, Command, IDLE, ERROR, WARN, BUSY, Drivable
|
||||||
|
|
||||||
|
|
||||||
|
class PeristalticPump(Drivable):
|
||||||
|
value = Parameter('Pump speed', FloatRange(0,100,unit="%"), default=0)
|
||||||
|
target = Parameter('Target pump speed', FloatRange(0,100,unit="%"), default=0)
|
||||||
|
status = Parameter()
|
||||||
|
|
||||||
|
addr_AO = Property('Address of the analog out', StringType())
|
||||||
|
addr_dir_relay = Property('Address of the direction relay', StringType())
|
||||||
|
addr_run_relay = Property('Address of the running relay', StringType())
|
||||||
|
|
||||||
|
direction = Parameter('pump direction', EnumType('direction', CLOCKWISE=0, ANTICLOCKWISE=1), default=0, readonly=False)
|
||||||
|
active = Parameter('pump running', BoolType(), default=False, readonly=False)
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO+'_enabled', 'w') as f :
|
||||||
|
f.write('0')
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO+'_mode', 'w') as f :
|
||||||
|
f.write('V')
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO, 'w') as f :
|
||||||
|
f.write('0')
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO+'_enabled', 'w') as f :
|
||||||
|
f.write('1')
|
||||||
|
|
||||||
|
def shutdownModule(self):
|
||||||
|
'''Disable analog output'''
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO, 'w') as f :
|
||||||
|
f.write('0')
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO+'_enabled', 'w') as f :
|
||||||
|
f.write('0')
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO, 'r') as f :
|
||||||
|
raw_value = f.read().strip('\n')
|
||||||
|
value = (int(raw_value) / 5000) * 100
|
||||||
|
return value
|
||||||
|
|
||||||
|
def write_target(self, target):
|
||||||
|
raw_value = (target / 100)*5000
|
||||||
|
with open('/sys/class/ionopimax/analog_out/'+self.addr_AO, 'w') as f :
|
||||||
|
f.write(str(int(raw_value)))
|
||||||
|
return target
|
||||||
|
|
||||||
|
def read_direction(self):
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_dir_relay, 'r') as f :
|
||||||
|
raw_direction = f.read().strip('\n')
|
||||||
|
if raw_direction == '0' or raw_direction == 'F':
|
||||||
|
return 0
|
||||||
|
if raw_direction == '1' or raw_direction == 'S':
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write_direction(self, direction):
|
||||||
|
if direction == 0:
|
||||||
|
raw_direction = '0'
|
||||||
|
elif direction == 1:
|
||||||
|
raw_direction = '1'
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_dir_relay, 'w') as f :
|
||||||
|
f.write(raw_direction)
|
||||||
|
return direction
|
||||||
|
|
||||||
|
def read_active(self):
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_run_relay, 'r') as f :
|
||||||
|
raw_active = f.read().strip('\n')
|
||||||
|
if raw_active == '0' or raw_active == 'F':
|
||||||
|
return False
|
||||||
|
elif raw_active == '1' or raw_active == 'S':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def write_active(self, active):
|
||||||
|
if active == False:
|
||||||
|
raw_active = '0'
|
||||||
|
elif active == True:
|
||||||
|
raw_active = '1'
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_run_relay, 'w') as f :
|
||||||
|
f.write(raw_active)
|
||||||
|
return active
|
||||||
|
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_dir_relay, 'r') as f :
|
||||||
|
raw_direction = f.read().strip('\n')
|
||||||
|
with open('/sys/class/ionopimax/digital_out/'+self.addr_run_relay, 'r') as f :
|
||||||
|
raw_active = f.read().strip('\n')
|
||||||
|
|
||||||
|
if raw_direction == 'F' or raw_direction == 'S':
|
||||||
|
return ERROR, 'Fault on direction relay'
|
||||||
|
elif raw_active == 'F' or raw_active == 'S':
|
||||||
|
return ERROR, 'Fault on pump activation relay'
|
||||||
|
elif self.active == True:
|
||||||
|
return BUSY, 'Pump running'
|
||||||
|
else:
|
||||||
|
return IDLE, ''
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def stop(self):
|
||||||
|
self.write_active(False)
|
@ -62,7 +62,7 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
|||||||
|
|
||||||
encoder_mode = Parameter('how to treat the encoder', EnumType('encoder', NO=0, READ=1, CHECK=2),
|
encoder_mode = Parameter('how to treat the encoder', EnumType('encoder', NO=0, READ=1, CHECK=2),
|
||||||
default=1, readonly=False)
|
default=1, readonly=False)
|
||||||
check_limit_switches = Parameter('whethter limit switches are checked',BoolType(),
|
check_limit_switches = Parameter('whether limit switches are checked',BoolType(),
|
||||||
default=0, readonly=False)
|
default=0, readonly=False)
|
||||||
value = PersistentParam('angle', FloatRange(unit='deg'))
|
value = PersistentParam('angle', FloatRange(unit='deg'))
|
||||||
status = PersistentParam()
|
status = PersistentParam()
|
||||||
@ -90,6 +90,8 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
|||||||
status_bits = ['power stage error', 'undervoltage', 'overtemperature', 'active',
|
status_bits = ['power stage error', 'undervoltage', 'overtemperature', 'active',
|
||||||
'lower switch active', 'upper switch active', 'step failure', 'encoder error']
|
'lower switch active', 'upper switch active', 'step failure', 'encoder error']
|
||||||
|
|
||||||
|
_doing_reference = False
|
||||||
|
|
||||||
def get(self, cmd):
|
def get(self, cmd):
|
||||||
return self.communicate(f'{self.address:x}{self.axis}{cmd}')
|
return self.communicate(f'{self.address:x}{self.axis}{cmd}')
|
||||||
|
|
||||||
@ -178,10 +180,14 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
|||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
super().doPoll()
|
super().doPoll()
|
||||||
if self._running and not self.isBusy():
|
if self._running and not self.isBusy() and not self._doing_reference:
|
||||||
if time.time() > self._stopped_at + 5:
|
if time.time() > self._stopped_at + 5:
|
||||||
self.log.warning('stop motor not started by us')
|
self.log.warning('stop motor not started by us')
|
||||||
self.hw_stop()
|
self.hw_stop()
|
||||||
|
if self._doing_reference and self.get('=H') == 'E' :
|
||||||
|
self.status = IDLE, ''
|
||||||
|
self.target = 0
|
||||||
|
self._doing_reference = False
|
||||||
|
|
||||||
def read_status(self):
|
def read_status(self):
|
||||||
hexstatus = 0x100
|
hexstatus = 0x100
|
||||||
@ -207,6 +213,9 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
|||||||
if status[0] == ERROR:
|
if status[0] == ERROR:
|
||||||
self._blocking_error = status[1]
|
self._blocking_error = status[1]
|
||||||
return status
|
return status
|
||||||
|
if self._doing_reference and self.get('=H') == 'N':
|
||||||
|
status = BUSY, 'Doing reference run'
|
||||||
|
return status
|
||||||
return super().read_status() # status from state machine
|
return super().read_status() # status from state machine
|
||||||
|
|
||||||
def check_moving(self):
|
def check_moving(self):
|
||||||
@ -346,3 +355,10 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
|||||||
self.status = 'IDLE', 'after error reset'
|
self.status = 'IDLE', 'after error reset'
|
||||||
self._blocking_error = None
|
self._blocking_error = None
|
||||||
self.target = self.value # clear error in target
|
self.target = self.value # clear error in target
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def make_ref_run(self):
|
||||||
|
'''Do reference run'''
|
||||||
|
self._doing_reference = True
|
||||||
|
self.status = BUSY, 'Doing reference run'
|
||||||
|
self.communicate(f'{self.address:x}{self.axis}0-')
|
||||||
|
70
frappy_psi/rheo_trigger.py
Normal file
70
frappy_psi/rheo_trigger.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
from frappy.core import StringType, BoolType, Parameter, Property, PersistentParam, Command, IDLE, ERROR, WARN, Writable
|
||||||
|
import time
|
||||||
|
|
||||||
|
class RheoTrigger(Writable):
|
||||||
|
addr = Property('Port address', StringType())
|
||||||
|
value = Parameter('Output state', BoolType(), default=0)
|
||||||
|
target = Parameter('target', BoolType(), default=0, readonly=False)
|
||||||
|
status = Parameter()
|
||||||
|
doBeep = Property('Make noise', BoolType(), default=0)
|
||||||
|
|
||||||
|
_status = 0
|
||||||
|
|
||||||
|
def initModule(self):
|
||||||
|
super().initModule()
|
||||||
|
with open('/sys/class/ionopimax/digital_io/'+self.addr+'_mode', 'w') as f :
|
||||||
|
f.write('out')
|
||||||
|
|
||||||
|
if self.doBeep:
|
||||||
|
with open('/sys/class/ionopimax/buzzer/beep', 'w') as f :
|
||||||
|
f.write('200 50 3')
|
||||||
|
|
||||||
|
def read_value(self):
|
||||||
|
with open('/sys/class/ionopimax/digital_io/'+self.addr, 'r') as f :
|
||||||
|
file_value = f.read()
|
||||||
|
if file_value == '0\n':
|
||||||
|
value = False
|
||||||
|
self._status = 0
|
||||||
|
elif file_value == '1\n':
|
||||||
|
value = True
|
||||||
|
self._status = 1
|
||||||
|
else:
|
||||||
|
self._status = -1
|
||||||
|
value = False
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def write_target(self,target):
|
||||||
|
if target == self.value:
|
||||||
|
return target
|
||||||
|
else:
|
||||||
|
with open('/sys/class/ionopimax/digital_io/'+self.addr, 'w') as f :
|
||||||
|
if target == True:
|
||||||
|
f.write('1')
|
||||||
|
elif target == False:
|
||||||
|
f.write('0')
|
||||||
|
time.sleep(0.05)
|
||||||
|
if self.doBeep:
|
||||||
|
with open('/sys/class/ionopimax/buzzer/beep', 'w') as f :
|
||||||
|
f.write('200')
|
||||||
|
self.status = self.read_status()
|
||||||
|
return target
|
||||||
|
|
||||||
|
def read_status(self):
|
||||||
|
self.value = self.read_value()
|
||||||
|
if self._status == 0:
|
||||||
|
return IDLE, 'Signal low'
|
||||||
|
elif self._status == 1:
|
||||||
|
return IDLE, 'Signal high'
|
||||||
|
else:
|
||||||
|
return ERROR, 'Cannot read status'
|
||||||
|
|
||||||
|
|
||||||
|
@Command
|
||||||
|
def toggle(self):
|
||||||
|
"""Toggle output"""
|
||||||
|
value = self.read_value()
|
||||||
|
if value == True:
|
||||||
|
self.write_target(False)
|
||||||
|
else:
|
||||||
|
self.write_target(True)
|
Reference in New Issue
Block a user