# # Template driver for the Knauer BlueShadow Pump 40P # vim: ft=tcl ts=8 sts=2 sw=2 expandtab autoindent smartindent nocindent # driver knauer_pump = { debug_threshold = 0; vendor = knauer; device = pump40p; protocol = knauer_ap; class = environment; simulation_group = environment_simulation; # group dummy = { type = text; readable = 1; data = false; control = false; nxsave = false; var status = { read_command = 'STATUS?'; read_function = read_status; property real_data = ' '; } var glp = { read_command = 'GLP?'; read_function = read_glp; property real_data = ' '; } } group pump = { var remote = { type = int; readable = 1; read_command = 'REMOTE?'; read_function = remote_read; writeable = 1; write_function = remote_write; allowed = '0,1'; } var state = { type = text; readable = 1; read_command = ' '; fetch_function = state_fetch; } var status = { type = text; readable = 1; read_command = ' '; fetch_function = status_fetch; } group volume = { var pval = { type = float; readable = 1; read_command = ' '; fetch_function = volume_fetch; read_function = volume_read; property 'units' = 'mL'; } var setp = { type = float; writeable = 1; write_command = ' '; write_function = volume_write; check_function = volume_check; driveable = pump/volume/pval; checkstatus_function = volume_checkstatus; halt_function = volume_halt; lowerlimit = 0; upperlimit = 100; tolerance = 0.01; readable = 1; read_command = ' '; fetch_function = volume_fsm; read_function = volume_store; property 'units' = 'mL'; property this_state = 0; } } group ratio = { var pval = { type = text; readable = 1; read_command = ' '; fetch_function = ratio_fetch; property 'units' = 'percent'; } var setp = { type = text; value = '25/25/25/25'; writeable = 1; write_command = ' '; write_function = ratio_write; checkrange_function = ratio_check; property 'units' = 'percent'; } } group flow = { var pval = { type = float; readable = 1; read_command = ' '; fetch_function = flow_fetch; property 'units' = 'mL/min'; } var setp = { type = float; value = 1.0; writeable = 1; write_command = ' '; write_function = flow_write; lowerlimit = 0; upperlimit = 9.999; property 'units' = 'mL/min'; } } } # # Ensure the pump starts up in REMOTE mode # code mkDriver = {%% #hset ${scobj_hpath}/pump/remote 1 %%} # # These functions handle the real_data returned by the pump for the GLP? command # code read_glp = {%% if { [string equal -nocase -length 6 ${data} "ERROR:"] } { } else { set dlist [split [lindex [split ${data} ":"] 1] ","] sct real_data "[join [lrange ${dlist} 0 end] ,]" set data "Hidden in real_data property" } %%} # # These functions handle the real_data returned by the pump for the STATUS? command # code read_status = {%% set dlist [split [lindex [split ${data} ":"] 1] ","] sct real_data "[join [lrange ${dlist} 0 end] ,]" set data "Hidden in real_data property" %%} # # Decode the status from the real_data to PUMPING if it is pumping else IDLE # code status_fetch = {%% set index 1 set data [hgetpropval ${tc_root}/dummy/status real_data] set dlist [split ${data} ","] set status_code [lindex ${dlist} ${index}] set cmd "@@NOSEND@@" if { ${status_code} == 3 } { sct result "PUMPING" } else { sct result "IDLE" } %%} # # Decode the state from the real_data to one of the documented strings # code state_fetch = {%% set index 1 set data [hgetpropval ${tc_root}/dummy/status real_data] set dlist [split ${data} ","] if { [llength ${dlist}] > ${index} } { set state_code [lindex ${dlist} ${index}] } else { set state_code "0" } set cmd "@@NOSEND@@" if { ${state_code} < 0 || ${state_code} > 9 } { sct geterror "Invalid device_state ${state_code}" error "[sct geterror]" } set slist [list "SYS_ST_INITIALIZING" \ "SYS_ST_OFF" \ "SYS_ST_IDLE" \ "SYS_ST_RUN" \ "SYS_ST_HOLD" \ "SYS_ST_PURGE" \ "SYS_ST_STANDBY" \ "SYS_ST_SEVEN" \ "SYS_ST_FAILED" \ "SYS_ST_RUNATEND" \ ] sct result [lindex ${slist} ${state_code}] %%} # # code flow_fetch = {%% set index 4 set data [hgetpropval ${tc_root}/dummy/status real_data] set dlist [split ${data} ","] if { [llength ${dlist}] > ${index} } { set flow_pv [lindex ${dlist} 4] } else { set flow_pv 0.0 } sct result [expr {0.001 * ${flow_pv}}] set cmd "@@NOSEND@@" %%} code flow_write = {%% set data [sct target] set cmd "@@NOSEND@@" set nextState idle if { ${data} != [sct oldval] } { debug_log ${tc_root} 1 "[sct] changed to new:${data}, from old:[sct oldval]" sct oldval ${data} sct update ${data} sct utime readtime } %%} # # code ratio_check = {%% set rlist [split ${setpoint} /] if { [llength ${rlist}] != 4 } { sct geterror "${setpoint} has [llength ${rlist}] components, needs 4" error [sct geterror] } set sum 0 for {set i 0} {$i < 4} {incr i} { set cmp [lindex ${rlist} ${i}] if { ![string is integer -strict ${cmp}] } { sct geterror "component [expr {${i} + 1}] is not integer: \"${cmp}\"" error [sct geterror] } if { !(${cmp} >= 0 && ${cmp} <= 100) } { sct geterror "component [expr {${i} + 1}] is not between 0 and 100: \"${cmp}\"" error [sct geterror] } set sum [expr {${sum} + ${cmp}}] } if { ${sum} != 100 } { sct geterror "sum of components is ${sum}, must be 100" error [sct geterror] } %%} code ratio_fetch = {%% set data [hgetpropval ${tc_root}/dummy/status real_data] set dlist [split ${data} ","] set ratio_vals "[lindex ${dlist} 5]/[lindex ${dlist} 6]/[lindex ${dlist} 7]/[lindex ${dlist} 8]" sct result ${ratio_vals} set cmd "@@NOSEND@@" %%} code ratio_write = {%% set data [sct target] set cmd "@@NOSEND@@" set nextState idle if { ${data} != [sct oldval] } { debug_log ${tc_root} 1 "[sct] changed to new:${data}, from old:[sct oldval]" sct oldval ${data} sct update ${data} sct utime readtime } %%} # # code remote_read = {%% if { [string equal -length 7 ${data} "REMOTE:"] } { set data [lindex [split ${data} :] 1] } else { sct geterror "bad response" error "[sct geterror]" } %%} code remote_write = {%% if { ${par} == 0 } { set cmd "LOCAL" } else { set cmd "REMOTE" } %%} # # code volume_fetch = {%% set data [hgetpropval ${tc_root}/dummy/glp real_data] set dlist [split ${data} ","] if { [llength ${dlist}] > 11 } { set pump_volm [lindex ${dlist} 10] set pump_voln [lindex ${dlist} 11] set pump_volume [expr {${pump_volm} + 0.000001 * ${pump_voln}}] } else { set pump_volume 0.0 } sct raw_volume ${pump_volume} if { [hpropexists [sct] base_volume] } { set pump_volume [expr {${pump_volume} - [sct base_volume]}] } elseif { [hpropexists [sct] raw_volume] } { sct base_volume [sct raw_volume] } sct result [format "%.2f" ${pump_volume}] set cmd "@@NOSEND@@" %%} code volume_write = {%% hset ${tc_root}/[sct driveable] 0.0 set cmd "REMOTE" sct this_state 1 sct pumping 1 set data ${par} if { ${data} != [sct oldval] } { debug_log ${tc_root} 1 "[sct] changed to new:${data}, from old:[sct oldval]" sct oldval ${data} sct update ${data} sct utime readtime } %%} code volume_fsm = {%% if { [sct this_state] > 0 } { set flow_tgt [expr {int(1000.0 * [hval ${tc_root}/pump/flow/setp])}] set ratio_tgt [join [split [hval ${tc_root}/pump/ratio/setp] /] ,] set time_tgt [expr {int(60000.0 * 1000.0 * [sct target] / ${flow_tgt})}] set time_1 [expr {${time_tgt} - 500}] set time_2 [expr {${time_tgt} + 500}] set saveState ${nextState} set nextState "noResponse" if { [sct this_state] == 1 } { set cmd "GLP?" set nextState ${saveState} sct this_state [expr {[sct this_state] + 1}] } elseif { [sct this_state] == 2 } { set cmd "TT_LOAD:1" sct this_state [expr {[sct this_state] + 1}] } elseif { [sct this_state] == 3 } { set cmd "TT_SET:0,0,${flow_tgt},${ratio_tgt},0,0,0,0,0,0,0,0" sct this_state [expr {[sct this_state] + 1}] } elseif { [sct this_state] == 4 } { set cmd "TT_SET:1,${time_1},${flow_tgt},${ratio_tgt},0,0,0,0,0,0,0,0" sct this_state [expr {[sct this_state] + 1}] } elseif { [sct this_state] == 5 } { set cmd "TT_SET:2,${time_2},0,${ratio_tgt},0,0,0,0,0,0,0,0" sct this_state [expr {[sct this_state] + 1}] } elseif { [sct this_state] == 6 } { set cmd "START:1,0" sct this_state 0 } elseif { [sct this_state] == 91 } { set cmd "STOP:1,0" sct this_state 92 } elseif { [sct this_state] == 92 } { set cmd "STOP:0,0" sct this_state 93 } elseif { [sct this_state] == 93 } { if { !([hpropexists ${tc_root}/pump/remote target] && [hgetpropval ${tc_root}/pump/remote target] == 1) } { set cmd "LOCAL" } else { set cmd "@@NOSEND@@" } sct this_state 0 } else { sct this_state 0 set cmd "@@NOSEND@@" set nextState idle } } else { set cmd "@@NOSEND@@" set nextState idle if { [hpropexists [sct] pumping] && [sct pumping] } { set new_value [hval ${tc_root}/pump/status] set old_value [sct oldval] if {${new_value} != ${old_value}} { sct oldval ${new_value} if {${old_value} == "PUMPING" && ${new_value} == "IDLE"} { set cmd "STOP:0,0" sct this_state 91 set nextState noResponse sct result "" sct driving 0 sct pumping 0 } } } } %%} code volume_store = {%% if { [sct this_state] == 2 } { set ns [namespace current] # store the GLP result hsetprop ${tc_root}/dummy/glp result "${data}" sct with ${tc_root}/dummy/glp "${ns}::read_glp ${tc_root}" # extract the volume sct with ${tc_root}/[sct driveable] "${ns}::volume_fetch ${tc_root} ${nextState} @@NOSEND@@" sct with ${tc_root}/[sct driveable] "${ns}::volume_read ${tc_root}" # copy it to base_volume hsetprop ${tc_root}/[sct driveable] base_volume [hgetpropval ${tc_root}/[sct driveable] raw_volume] } if { [hpropexists [sct] target] } { set data [sct target] } else { set data 0.0 } %%} code volume_halt = {%% set cmd "STOP:0,0" sct this_state 91 debug_log ${tc_root} 1 "volume_halt sct send ${cmd}" sct send ${cmd} %%} }