######################################################################
############# ############
############# macros for PSD 8 Hamilton syringe drive. ############
############# ############
######################################################################
# Fri 22 Feb 2008 13:32:13
# Wed 7 Nov 2012 16:26:53
# 2014/10/10 rh - updated changes made by CG for id03 and fixed for ID15
#%TITLE%
#%NAME%
#%END%
#%DESCRIPTION%
#%EXAMPLE%
######################################################################
########################## ##########################
########################## SETUP AND INIT ##########################
########################## ##########################
######################################################################
need hg
hg_config_style("psd8", 1, 2, 3)
hg_generate("psd8")
#%UU% <serial_number> <syringe1_model> [<syringe2_model>]*
#%MDESC%
# This macro does the setup procedure to use one or more PSD8 hamilton syringe.
# Example to setup 3 syringes pluged on serial line 17.
# psd8_setup 17 1001 1005 1025
#
def psd8_setup '{
global PSD8_PAR[]
global PSD8_ARGS
unglobal PSD8_CONF
global PSD8_CONF
list_init PSD8_CONF
local _model _volume _nos
local res
local nbparam
nbparam = $#
# PSD8_PAR["serial"] = Serial line number in spec.
# PSD8_PAR["on"] = PSD control active or not.
# PSD8_PAR["timeout"] = Timeout value (in seconds).
#### PSD8_PAR[1]["rate"] = PSD_PAR[1]["Syringe"]/PSD_PAR[1]["MaxVelocity"]
#### PSD8_PAR[1]["volume"] = the current volume of solution in syringe
# PSD8_PAR[1]["maxvelocity"] = 1.2-600 (table 8-33)
# PSD8_PAR[1]["speedcode"] = 1-40 (table 8-33)
# PSD8_PAR[1]["maxsteps"] = 3000 (standard) or 24000 (hi-resolution)
# PSD8_PAR[1]["port"] = 1,2,3... (depend on the valve model)
# PSD8_PAR[1]["model"] = model number, in {1001 ; 1002 ; 1005 ; 1010 ; 1025}
# Volume of the syringe given the model number.
PSD8_PAR["volume"]["1001"] = 1
PSD8_PAR["volume"]["1002"] = 2.5
PSD8_PAR["volume"]["1005"] = 5
PSD8_PAR["volume"]["1010"] = 10
PSD8_PAR["volume"]["1025"] = 25
# General parameters or constants.
PSD8_PAR["model_string"] = "1001 1002 1005 1010 1025"
PSD8_PAR["timeout"] = 10
PSD8_PAR["serial"] = 0
PSD8_PAR["maxsteps"] = 3000
PSD8_PAR["maxsteps_highres"] = 24000
PSD8_PAR["sleep_time"] = 0.2
if(nbparam == 0){
print "psd8_setup usage:"
print " psd8_setup <serial> [<model>]*"
print " "
print " NB: configuration of serial line : "
print " -9600 or 38400 bps"
print " -8 data bits"
print " -No parity"
print " -1 stop bit"
exit
}
if(nbparam < 2) {
psd8_err("psd8_setup need more than 1 arguments")
exit
}
if ($1 < 0) {
psd8_err("serial line number must be positif")
exit
}
if ($1 > 32){
psd8_err("Achtung : the serial line number is strange (>32)")
}
PSD8_PAR["serial"] = $1
res = split ("$*", PSD8_ARGS)
_nos = res-1
psd8_msg("----------- PSD8 syringe(s) setup -----------")
for (i=0 ; i<_nos ; i++){
_model = PSD8_ARGS[i+1]
_volume = PSD8_PAR["volume"][_model]
psd8_msg(sprintf( "setup syringe %d : model=%s volume=%g mL", \
i+1, _model, _volume))
if (!index(PSD8_PAR["model_string"], _model)){
psd8_err(sprintf("syringe %d is not a valid model", i+1))
exit
}
_name = sprintf("syringe%d", i+1)
list_add(PSD8_CONF, _name)
list_setpar(PSD8_CONF, _name, "model", _model)
list_setpar(PSD8_CONF, _name, "volume", _volume)
list_setpar(PSD8_CONF, _name, "address", i+1)
}
psd8_msg("---------------- setup done -----------------")
psd8_init_all()
psd8_on
}'
#%UU% <parma>
#%MDESC%
#
def psd8_unsetup'{
psd8_off
unglobal PSD8_PAR
unglobal PSD8_ARGS
unglobal PSD8_CONF
}'
#%UU% <parma>
#%MDESC%
#
def psd8_on '{
global PSD8_PAR[]
PSD8_PAR["on"] = 1
psd8_msg("PSD8 is now on")
}'
#%UU% <parma>
#%MDESC%
#
def psd8_off '{
global PSD8_PAR[]
PSD8_PAR["on"] = 0
psd8_msg("PSD8 is now off")
}'
#%IU% <parma>
#%MDESC%
# Calls psd8_init for all syringes.
def psd8_init_all() '{
local i, _syr_num, _nos
_nos = list_n(PSD8_CONF)
for (i=0; i<_nos; i++){
_syr_num = i+1
psd8_init(_syr_num)
}
}'
#%IU% <parma>
#%MDESC%
# Init of one syringe pump.
def psd8_init(syr_num) '{
global PSD8_PAR[]
global PSD8_CONF[]
local _pos _name
_name = PSD8_CONF[syr_num]
# + initialization
# - syringe volume/full stroke (global variable)
# - with or without valve configuration
# - at [full/half/quarter/...] speed
# - set motor resolution
# Flush (in and out) the serial line.
ser_par(PSD8_PAR["serial"], "flush", 2)
# initialize PSD8 assign valve output to right
# _psd8_send(syr_num, "ZR")
# initialize PSD8 assign valve output to left
# _psd8_send(syr_num, "XR")
# initialize PSD8: configure for no valve
_psd8_send(syr_num, "WR")
}'
######################################################################
############################# ############################
############################# MOVEMENTS ############################
############################# ############################
######################################################################
# Moves given syringe to absolute position <position>.
def psd8_mv(syr_num, position) '{
global PSD8_PAR[]
local _maxsteps
_maxsteps = _psd8_get_maxsteps(syr_num)
if (( position < 0 ) || (position > _maxsteps)){
psd8_err(sprintf (" move out of range : %d. Range is [0..%d]", \
position , _maxsteps ))
exit
}
_psd8_send(syr_num, sprintf ("A%dR", position))
}'
# Move given syringe of <movement> steps.
# + to fill
# - to push out
def psd8_mvr(syr_num, movement) '{
global PSD8_PAR[]
local _maxsteps _position
_maxsteps = _psd8_get_maxsteps(syr_num)
_position = _psd8_get_position(syr_num)
if ((_position + movement) > _maxsteps) {
psd8_err(sprintf (" move out of range : %d. Range is [0..%d]\n", \
_position+movement, PSD8_PAR["maxsteps"] ))
exit
}
# D -> up. => negative move to dispense.
# P -> down. => positif move to draw/pickup.
if(movement < 0 ){
psd8_dbg(sprintf("psd8_mvr : D %d", -movement))
_psd8_send(syr_num, sprintf ("D%dR", -movement))
}
else{
psd8_dbg(sprintf("psd8_mvr : P %d", movement))
_psd8_send(syr_num, sprintf ("P%dR", movement))
}
}'
#%UU% (<syr_num>, <volume>)
#%MDESC%
# Fills <volume> ml of liquid into the syringe.
def psd8_draw(syr_num, volume)'{
global PSD8_PAR[]
local volume_inside
local vol_max
local delta_steps
local _name
local _position
if (volume < 0){
psd8_err("psd8_draw - volume must be positif")
exit
}
_name = PSD8_CONF[syr_num]
# test position
_position = _psd8_get_position(syr_num)
volume_inside = _psd8_steps_to_volume(syr_num, _position)
vol_max = PSD8_CONF["syringe1"]["volume"]
psd8_dbg(sprintf("_name=%s volume_inside=%f vol_max=%g",\
_name, volume_inside, vol_max ))
if (volume_inside + volume > vol_max){
psd8_err("Achtung: You syringe is too small")
exit
}
else{
delta_steps = _psd8_volume_to_steps(syr_num, volume)
psd8_mvr(syr_num, delta_steps)
return (0)
}
}'
# Pushout/dispense <volume> mL of liquid from the syringe <syringe_number>.
def psd8_dispense(syr_num, volume) '{
global PSD8_PAR[]
local _volume_inside
local _delta_steps
local _name
if (volume<0){
psd8_err("volume must be positif")
exit
}
_name = sprintf("syringe%d", syr_num)
# test position
_volume_inside = _psd8_steps_to_volume(syr_num, \
_psd8_get_position(syr_num))
psd8_dbg(sprintf("psd8_dispense : _name=%s volume_inside=%f volume=%g",\
_name, _volume_inside, volume ))
if (_volume_inside < volume){
psd8_err("There is not enough liquid inside syringe")
exit
}
else{
_delta_steps = _psd8_volume_to_steps(syr_num, volume)
psd8_dbg(sprintf("psd8_dispense : mvr(-%f)", _delta_steps))
psd8_mvr(syr_num, -_delta_steps)
return (0)
}
}'
######################################################################
########################### ##########################
########################### VALVE ACTIONS ##########################
########################### ##########################
######################################################################
def psd8_valve_to_input_pos(syr_num) '{
global PSD8_PAR[]
_psd8_send(syr_num, "IR")
}'
def psd8_valve_to_output_pos(syr_num) '{
global PSD8_PAR[]
_psd8_send(syr_num, "OR")
}'
def psd8_valve_to_bypass_pos(syr_num) '{
global PSD8_PAR[]
_psd8_send(syr_num, "BR")
}'
def psd8_valve_to_extra_pos(syr_num) '{
global PSD8_PAR[]
_psd8_send(syr_num, "ER")
}'
######################################################################
###################### ######################
###################### MOTOR CONTROL COMMANDS ######################
###################### ######################
######################################################################
#%IU% (<syr_num>)
#%MDESC%
#
def psd8_mot_set_resolution_high(syr_num) '{
global PSD8_PAR[]
local _name
_name = sprintf("syringe%d", syr_num)
_psd8_send(syr_num, "N1R")
PSD8_CONF[_name]["maxsteps"] = PSD8_PAR["maxsteps_highres"]
list_setpar(PSD8_CONF, _name, "resolution", "high")
}'
#%IU% (<syr_num>)
#%MDESC%
#
def _psd8_get_maxsteps(syr_num) '{
local _maxsteps _name
_name = sprintf("syringe%d", syr_num)
if(list_getpar(PSD8_CONF, _name, "resolution") == "high"){
_maxsteps = PSD8_PAR["maxsteps_highres"]
}
else{
_maxsteps = PSD8_PAR["maxsteps"]
}
return(_maxsteps)
}'
#%IU% (<syr_num>)
#%MDESC%
#
def psd8_mot_set_resolution_normal(syr_num) '{
global PSD8_PAR[]
local _name
_name = sprintf("syringe%d", syr_num)
_psd8_send(syr_num, "N0R")
PSD8_CONF[_name]["maxsteps"] = PSD8_PAR["maxsteps"]
list_setpar(PSD8_CONF, _name, "resolution", "normal")
}'
# <speed> in [1; 40]
def psd8_mot_set_speed(syr_num, speed) '{
global PSD8_PAR[]
global PSD8_CONF
local _name
_name = sprintf("syringe%d", syr_num)
if((speed<0) || (speed>40)){
psd8_err("speed outside range [0;40]")
exit
}
_psd8_send(syr_num, sprintf("S%dR", speed))
PSD8_CONF[_name]["speed"] = speed
}'
######################################################################
############################### ##############################
############################### UTILS ##############################
############################### ##############################
######################################################################
# Converts <steps> to volume for syringe <syr_num>
def _psd8_steps_to_volume(syr_num, steps) '{
global PSD8_PAR
global PSD8_CONF
local _volume
local _vol_max
local _maxstep
local _name
_name = sprintf("syringe%d", syr_num)
_vol_max = PSD8_CONF[_name]["volume"]
_maxstep = _psd8_get_maxsteps(syr_num)
if (_maxstep == 0) {
psd8_err("error : _maxstep=0 ")
exit
}
_volume = steps * _vol_max / _maxstep
return (_volume)
}'
# Converts <volume> to steps for syringe <syr_num>
def _psd8_volume_to_steps(syr_num, volume) '{
global PSD8_PAR[]
local _steps
local _vol_max
local _maxstep
local _name
_name = sprintf("syringe%d", syr_num)
_vol_max = PSD8_CONF[_name]["volume"]
_maxstep = _psd8_get_maxsteps(syr_num)
if(_vol_max == 0) {
psd8_err(" vol_max=0")
exit
}
_steps = volume * _maxstep / _vol_max
return (_steps)
}'
# Reads and returns the position (in steps) of a given syringe.
def _psd8_get_position(syr_num) '{
global PSD8_PAR[]
local _position _maxsteps _answer _name
_name = sprintf("syringe%d", syr_num)
_answer = _psd8_send(syr_num, "?4")
_position = _answer + 0
_maxsteps = _psd8_get_maxsteps(syr_num)
if((_position < 0) || (_position > _maxsteps)) {
psd8_err(sprintf("The position seems strange : %s", _position))
}
return (_position)
}'
# Reads the position and returns the current volume (in mL) of a given syringe.
def _psd8_get_volume(syr_num) '{
global PSD8_PAR[]
local _volume
local _answer
_answer = _psd8_get_position(syr_num)
_volume = _psd8_steps_to_volume(syr_num, _answer + 0)
return (_volume)
}'
######################################################################
########################### ##########################
########################### COMMUNICATION ##########################
########################### ##########################
######################################################################
#
def _psd8_send(syr_num, cmd) '{
global PSD8_PAR[]
local _answer _strcmd _address _name
if(PSD8_PAR["on"]){
# command form is : /<address><data><CR>
_name = PSD8_CONF[syr_num]
_address = PSD8_CONF[_name]["address"]
# There is a "/" at beginning of command and "\r" at the end:
_strcmd = sprintf("/%d%s\r", _address, cmd)
# _strcmd = sprintf("%c%s\r", _address+96, cmd)
# printf("strcmd =\"%s\"\n", _strcmd)
ser_par(PSD8_PAR["serial"], "flush", 2)
ser_put(PSD8_PAR["serial"], _strcmd)
sleep(PSD8_PAR["sleep_time"])
_answer = ser_get(PSD8_PAR["serial"], "\r")
psd8_dbg(sprintf("_psd8_send : _answer = %s", _answer))
if (_answer == "") {
psd8_err("no answer from controller")
}
# if /0@ =>ok
global PSD8_TEMP
PSD8_TEMP[0] = 0
split(_answer, PSD8_TEMP, "`")
PSD8_TEMP[2] = removeEndingChar(PSD8_TEMP[1], "\r")
PSD8_TEMP[3] = removeEndingChar(PSD8_TEMP[2], "\003")
return PSD8_TEMP[3]
}
else{
psd8_msg("PSD is OFF")
}
}'
######################################################################
############################### ##############################
############################### INFOS ##############################
############################### ##############################
######################################################################
#
#%MDESC%
# Prints infos for all syringes.
def psd8_info '{
global PSD8_PAR[]
global PSD8_CONF[]
local ii _nos _name _syr_num
_nos = list_n(PSD8_CONF)
for (ii=0 ; ii<_nos ; ii++){
_syr_num = ii+1
psd8_infos(_syr_num)
}
}'
#
#%MDESC%
# Prints infos dfor syringe <syr_num>
def psd8_infos(syr_num) '{
global PSD8_PAR[]
psd8_msg(sprintf( "FIRMWARE VERSION : %s", _psd8_firmware_version(syr_num)))
psd8_msg(sprintf( "PUMP STATUS: %s ", _psd8_pump_status(syr_num)))
psd8_msg(sprintf( "ABSOLUTE POSITION: %s", _psd8_get_abs_pos(syr_num)))
}'
#%IU% ()
#%MDESC%
# This macro returns the firmware number of controller.
def _psd8_firmware_version(syr_num) '{
return _psd8_send(syr_num, "&")
}'
#%IU% ()
#%MDESC%
# Returns the status of the pump.
def _psd8_pump_status(syr_num) '{
return _psd8_send(syr_num, "Q")
}'
#%IU% ()
#%MDESC%
# Returns the absolute syringe position.
def _psd8_get_abs_pos(syr_num) '{
return _psd8_send(syr_num, "?")
}'
#%UU%
#%MDESC%
# Returns 1 if pump is busy 0 otherwise.
def psd8_busy(syr_num) '{
local _ans
_ans = _psd8_pump_status(syr_num)
if(_ans=="/0@\003\r" ){
return(1)
}
else if (_ans == "/0`\003\r"){
return(0)
}
else {
psd8_err("unknown status ????")
return(1)
}
}'
# moving :
# 791.ID26CTL> TTT[0] = _psd8_pump_status(3)
# "trcmd ="/3Q
# 792.ID26CTL> p TTT
# TTT["0"] = "/0@\003\r"
# not moving:
# 793.ID26CTL> TTT[0] = _psd8_pump_status(3)
# "trcmd ="/3Q
# 794.ID26CTL> p TTT
# TTT["0"] = "/0`\003\r"
# CYRIL> ser_par(1,"flush",2)
# CYRIL> ser_put(1,"/1XR\r")
# CYRIL> ser_put(1,"/2ZR\r")
# CYRIL> ser_put(1,"/3ZR\r")
# CYRIL> ser_put(1,"/1A1100R\r")
# CYRIL> ser_put(1,"/2A1500R\r")
# CYRIL> ser_put(1,"/3A1900R\r")
# 237.CYRIL> TTT[0] = _psd8_send(1, "?")
# "trcmd ="/1?
# 238.CYRIL> p TTT
# TTT["0"] = "/0`1100\003\r"
# 239.CYRIL> TTT[0] = _psd8_send(2, "?")
# "trcmd ="/2?
# 240.CYRIL> p TTT
# TTT["0"] = "/0`1500\003\r"
# 241.CYRIL> TTT[0] = _psd8_send(3, "?")
# "trcmd ="/3?
# 242.CYRIL> p TTT
# TTT["0"] = "/0`1900\003\r"
# 245.CYRIL> p length(TTT[0])
# 9
# man ascii
# 003 3 03 ETX
|