# # Simple driver generator for the Hiden Isochema XCS vapour delivery system # vim: ts=8 sts=2 sw=2 expandtab autoindent smartindent nocindent # driver hiden_xcs = { # driver_property douglas = clowes vendor = hiden; device = xcs; protocol = std; class = environment simulation_group = environment_simulation add_args = 'terminator {tol 0.5}'; make_args = 'tol'; protocol_args = '${terminator}'; # # Unnamed group has variables at device level # group = { # group_property douglas = clowes type = float priv = user group_property 'data' = 'true' group_property 'nxsave' = 'true' property 'klass' = 'environment' property 'sdsinfo' = '::nexus::scobj::sdsinfo' var enabled = { type = int; priv = user; readable = 10; writeable = 1; read_function = read_digital; write_Function = write_digital; read_command = '?DOUT,2'; write_command = '!DOUT,2,'; allowed = '0,1'; readable = 10; # property junk = junk; } var flow = { driveable = flow mutable = true readable = 1 read_command = 'None' fetch_function = fetch_flow read_function = read_flow write_function = write_flow lowerlimit = 0 upperlimit = 500 tolerance = 1 pid_function = pid_flow property pid_error = 0 property pid_deriv = 0 property pid_integ = 0 property pid_pvalue = 0.2 property pid_ivalue = 0.1 property pid_dvalue = 0.0 property pid_imax = 30 } var humidity = { driveable = humidity mutable = true readable = 1 read_command = '?ALL DATA' read_function = read_all_data write_function = write_humidity checkrange_function = chkrange_function value = 50 lowerlimit = 10 upperlimit = 90 tolerance = 1 pid_function = pid_humidity property pid_error = 0 property pid_deriv = 0 property pid_integ = 0 property pid_pvalue = 0.2 property pid_ivalue = 0.1 property pid_dvalue = 0.0 property pid_imax = 30 property settle_time = 5.250 } var gas_factor = { value = 1.0; } control = false data = false nxsave = false var temperature var flow1 var flow2 var flow3 } # # The named group is at the device level, variables below that # group analog = { # group_property 'data' = true type = float; priv = user; control = false data = false nxsave = false readable = 5; read_function = read_sixteen; property base = 0; # applies to all following vars var pv1 = { read_command = '?AIN,0'; property span = 500; }; # ain0 var pv2 = { read_command = '?AIN,1'; property span = 500; }; # ain1 var pv3 = { read_command = '?AIN,2'; property span = 500; }; # ain2 var rhtemp = { read_command = '?AIN,8'; property span = 100; }; # ain8 var rhsense = { read_command = '?AIN,9'; property span = 100; }; # ain9 var ansto_temp = { read_command = '?AIN,12'; property span = 100; }; # ain12 writeable = 1; read_function = read_twelve; write_function = write_twelve; var sp1 = { read_command = '?AOUT,0'; write_command = '!AOUT,0,'; property span = 500; } # aout0 var sp2 = { read_command = '?AOUT,1'; write_command = '!AOUT,1,'; property span = 500; } # aout1 var sp3 = { read_command = '?AOUT,2'; write_command = '!AOUT,2,'; property span = 500; } # aout2 }; # # Code lines start with '@' which is stripped before being emitted into generated driver # The code is emitted at the appropriate place in the given function # code read_function read_digital = { @ if { [string equal -nocase -length 5 "${data}" "DOUT ="] } { @ set result [scan "${data}" "DOUT = %d OK" val] @ if { ${result} == 1 } { @ set data ${val} @ } else { @ sct geterror "Syntax error (Result=${result}) in: '${data}'" @ } @ } else { @ sct geterror "Syntax error in: '${data}'" @ } } code read_function read_twelve = { @ if { [string equal -nocase -length 5 "${data}" "AOUT ="] } { @ set result [scan "${data}" "AOUT = %d OK" val] @ if { ${result} == 1 } { @ if { [hpropexists [sct] base] } { @ set base [sct base] @ } else { @ set base 0.0 @ } @ if { [hpropexists [sct] span] } { @ set span [sct span] @ } else { @ set span 500.0 @ } @ set data [expr (${span} * (${val} - ${base})) / 4095.0] @ } else { @ sct geterror "Syntax (Result=${result}) error in: '${data}'" @ } @ } else { @ sct geterror "Syntax error in: '${data}'" @ } } code read_function read_sixteen = { @ if { [string equal -nocase -length 5 "${data}" "AIN ="] } { @ set result [scan "${data}" "AIN = %d OK" val] @ if { ${result} == 1 } { @ if { [hpropexists [sct] base] } { @ set base [sct base] @ } else { @ set base 0.0 @ } @ if { [hpropexists [sct] span] } { @ set span [sct span] @ } else { @ set span 500.0 @ } @ set data [expr (${span} * (${val} - ${base})) / 65535.0] @ } else { @ sct geterror "Syntax error (Result=${result}) in: '${data}'" @ } @ } else { @ sct geterror "Syntax error in: '${data}'" @ } } code read_function read_all_data = { @ if { [string equal -nocase -length 2 "${data}" "A "] } { @ set data_list [split [string range "${data}" 2 end-3] ','] @ if { [llength ${data_list}] == 8 } { @ set data [expr [lindex ${data_list} 0]] @ hupdate ${tc_root}/temperature [expr [lindex ${data_list} 1]] @ hupdate ${tc_root}/flow1 [expr [lindex ${data_list} 3] / [hval ${tc_root}/gas_factor]] @ hupdate ${tc_root}/flow2 [expr [lindex ${data_list} 4] / [hval ${tc_root}/gas_factor]] @ hupdate ${tc_root}/flow3 [expr [lindex ${data_list} 5] / [hval ${tc_root}/gas_factor]] @ } else { @ sct geterror "Syntax error (Result=${result}) in: '${data}'" @ } @ } else { @ sct geterror "Syntax error for read_all_data in: '${data}'" @ } @ if { [hpropexists [sct] target] } { @ set pid [pid_humidity ${tc_root} [sct target] ${data}] } code pid_function pid_humidity = { @ set path [pathname [sct]] @ set sign 1 @ foreach node [list analog/sp1 analog/sp2] { @ set sign [expr -${sign}] @ set signed_pid [expr ${sign} * ${pid}] @ hsetprop ${path}/${node} bias_humidity ${signed_pid} @ if { [hpropexists ${path}/${node} target] } { @ hset ${path}/${node} [hgetpropval ${path}/${node} target] @ } else { @ hset ${path}/${node} [hval ${path}/${node}] @ } @ } @ } } code fetch_function fetch_flow = { @ set data 0.0 @ set targets 0.0 @ set path [pathname [sct]] @ foreach node [list flow1 flow2] { @ set data [expr ${data} + [hval ${path}/${node}]] @ } @ sct result ${data} @ foreach node [list analog/sp1 analog/sp2] { @ set targets [expr ${targets} + [hval ${path}/${node}]] @ } @ sct targets ${targets} @ if { [hpropexists [sct] target] } { @ set pid [pid_flow ${tc_root} [sct target] ${data}] @# cut this function short @ return ${nextState} } code pid_function pid_flow = { @ set path [pathname [sct]] @ foreach node [list analog/sp1 analog/sp2] { @ hsetprop ${path}/${node} bias_flow ${pid} @ if { [hpropexists ${path}/${node} target] } { @ hset ${path}/${node} [hgetpropval ${path}/${node} target] @ } else { @ hset ${path}/${node} [hval ${path}/${node}] @ } @ } @ } } code write_function write_digital = { } code write_function write_twelve = { @ if { [hpropexists [sct] base] } { @ set base [sct base] @ } else { @ set base 0.0 @ } @ if { [hpropexists [sct] span] } { @ set span [sct span] @ } else { @ set span 500.0 @ } @ if { [hpropexists [sct] bias_humidity] } { @ set par [expr ${par} + [sct bias_humidity]] @ } @ if { [hpropexists [sct] bias_flow] } { @ set par [expr ${par} + [sct bias_flow]] @ } @ if { ${par} < 0.0 } { @ set par 0.0 @ } @ if { ${par} > ${span} } { @ set par ${span} @ } @ set par [expr int(${base} + (4095.0 * ${par} / ${span}))] @ set cmd "${cmd_str}${par}" } code write_function write_flow = { @ if { [hpropexists ${tc_root}/humidity target] } { @ set humidity_target [hgetpropval ${tc_root}/humidity target] @ } else { @ set humidity_target [hval ${tc_root}/humidity] @ } @ set flow1_target [expr (0.01 * (100.0 - ${humidity_target})) * [sct target]] @ set flow2_target [expr (0.01 * ( ${humidity_target})) * [sct target]] @ hset ${tc_root}/analog/sp1 ${flow1_target} @ hset ${tc_root}/analog/sp2 ${flow2_target} @ set cmd "@@NOSEND@@" } code write_function write_humidity = { @ if { [hpropexists ${tc_root}/flow target] } { @ set flow_target [hgetpropval ${tc_root}/flow target] @ } else { @ set flow_target [hval ${tc_root}/flow] @ } @ set flow1_target [expr (0.01 * (100.0 - [sct target])) * ${flow_target}] @ set flow2_target [expr (0.01 * ( [sct target])) * ${flow_target}] @ hset ${tc_root}/analog/sp1 ${flow1_target} @ hset ${tc_root}/analog/sp2 ${flow2_target} @ set cmd "@@NOSEND@@" } code write_function no_op = { @ set cmd "@@NOSEND@@" } # # This code is after database creation # code mkDriver = { } code chkrange_function = { @ # hooked } };