Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
a7fd90cd6d |
@ -1,6 +1,6 @@
|
||||
Node('flowsas.psi.ch',
|
||||
'flowsas test motors',
|
||||
'tcp://5000',
|
||||
'tcp://3000',
|
||||
)
|
||||
|
||||
#Mod('mot_io',
|
||||
@ -14,7 +14,7 @@ Node('flowsas.psi.ch',
|
||||
# 'horizontal axis',
|
||||
# axis = 'X',
|
||||
# io = 'mot_io',
|
||||
# encoder_mode= 'NO',
|
||||
# encoder_mode = 'NO',
|
||||
# )
|
||||
|
||||
#Mod('vmot',
|
||||
@ -28,7 +28,7 @@ Node('flowsas.psi.ch',
|
||||
Mod('syr_io',
|
||||
'frappy_psi.cetoni_pump.LabCannBus',
|
||||
'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',
|
||||
@ -37,7 +37,7 @@ Mod('syr1',
|
||||
io='syr_io',
|
||||
pump_name = "Nemesys_S_1_Pump",
|
||||
valve_name = "Nemesys_S_1_Valve",
|
||||
inner_diameter_set = 10,
|
||||
inner_diameter_set = 14.5673,
|
||||
piston_stroke_set = 60,
|
||||
)
|
||||
|
||||
@ -47,6 +47,14 @@ Mod('syr2',
|
||||
io='syr_io',
|
||||
pump_name = "Nemesys_S_2_Pump",
|
||||
valve_name = "Nemesys_S_2_Valve",
|
||||
inner_diameter_set = 1,
|
||||
inner_diameter_set = 14.5673,
|
||||
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)
|
||||
|
||||
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 qmixpump
|
||||
from qmixsdk import qmixvalve
|
||||
from qmixsdk.qmixpump import ContiFlowProperty, ContiFlowSwitchingMode
|
||||
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")
|
||||
|
||||
def earlyInit(self):
|
||||
@ -22,11 +24,15 @@ class LabCannBus(Readable):
|
||||
super().initModule()
|
||||
self.bus.start()
|
||||
|
||||
with open('/sys/class/ionopimax/buzzer/beep', 'w') as f :
|
||||
f.write('200 50 3')
|
||||
|
||||
def shutdownModule(self):
|
||||
"""Not so gracefully close the connection"""
|
||||
"""Close the connection"""
|
||||
self.bus.stop()
|
||||
self.bus.close()
|
||||
|
||||
|
||||
class SyringePump(Drivable):
|
||||
io = Attached()
|
||||
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)
|
||||
piston_stroke_set = Property('piston stroke', FloatRange(), default=60)
|
||||
|
||||
value = PersistentParam('volume', FloatRange(unit='mL'))
|
||||
status = PersistentParam()
|
||||
value = Parameter('volume', FloatRange(unit='uL'))
|
||||
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)
|
||||
max_volume = Parameter('max volume', FloatRange(0,100000, unit='mL',), 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='mL'), readonly=False)
|
||||
target_flow_rate = Parameter('target flow rate', FloatRange(unit='uL/s'), readonly=False)
|
||||
real_flow_rate = Parameter('actual flow rate', FloatRange(unit='uL/s'), readonly=True)
|
||||
|
||||
target = Parameter('target volume', FloatRange(unit='uL'), readonly=False)
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
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_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_volume = self.pump.get_volume_max()
|
||||
self.no_of_valve_pos = self.valve.number_of_valve_positions()
|
||||
self.max_flow_rate = round(self.pump.get_flow_rate_max(),2)
|
||||
self.max_volume = round(self.pump.get_volume_max(),2)
|
||||
self.valve_pos = self.valve.actual_valve_position()
|
||||
|
||||
self.target_flow_rate = self.max_flow_rate * 0.5
|
||||
self.target = self.pump.get_fill_level()
|
||||
self.target_flow_rate = round(self.max_flow_rate * 0.5,2)
|
||||
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):
|
||||
return self.pump.get_fill_level()
|
||||
return round(self.pump.get_fill_level(),2)
|
||||
|
||||
def write_target(self, target):
|
||||
self.pump.set_fill_level(target, self.target_flow_rate)
|
||||
self.status = BUSY, 'Target changed'
|
||||
return target
|
||||
if self.read_valve_pos() == 0 :
|
||||
self.status = ERROR, 'Cannot pump if valve is closed'
|
||||
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):
|
||||
self.pump.target_flow_rate = rate
|
||||
self.target_flow_rate = rate
|
||||
return rate
|
||||
|
||||
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):
|
||||
return self.valve.actual_valve_position()
|
||||
@ -100,11 +123,165 @@ class SyringePump(Drivable):
|
||||
self.valve.switch_valve_to_position(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):
|
||||
fault_state = self.pump.is_in_fault_state()
|
||||
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:
|
||||
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:
|
||||
return BUSY, 'Pumping'
|
||||
else:
|
||||
@ -113,4 +290,24 @@ class SyringePump(Drivable):
|
||||
@Command
|
||||
def stop(self):
|
||||
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),
|
||||
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)
|
||||
value = PersistentParam('angle', FloatRange(unit='deg'))
|
||||
status = PersistentParam()
|
||||
@ -90,6 +90,8 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
||||
status_bits = ['power stage error', 'undervoltage', 'overtemperature', 'active',
|
||||
'lower switch active', 'upper switch active', 'step failure', 'encoder error']
|
||||
|
||||
_doing_reference = False
|
||||
|
||||
def get(self, 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):
|
||||
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:
|
||||
self.log.warning('stop motor not started by us')
|
||||
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):
|
||||
hexstatus = 0x100
|
||||
@ -207,6 +213,9 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
||||
if status[0] == ERROR:
|
||||
self._blocking_error = status[1]
|
||||
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
|
||||
|
||||
def check_moving(self):
|
||||
@ -346,3 +355,10 @@ class Motor(HasOffset, HasStates, PersistentMixin, HasIO, Drivable):
|
||||
self.status = 'IDLE', 'after error reset'
|
||||
self._blocking_error = None
|
||||
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