Files
sics/site_ansto/instrument/config/environment/sct_protek_common.tcl

256 lines
8.5 KiB
Tcl

##
# @file sct_protek_common.tcl
# @brief Gets display state from Protek 608 multimeters.
#
# Author: Ferdi Franceschini (ffr@ansto.gov.au)
## MULTIMETER STATE FIELDS AND VALUES
###### MODES
# AUTOOFF:1/0 PULSE:1/0 MAX:1/0 PLUSPEAK:1/0 REL:1/0 RECALL:1/0 GO/NG:1/0 MINUSPER:1/0
# RS232C:1/0 PLUS:1/0 MINUS:1/0 MIN:1/0 MINUSPEAK:1/0 AVG:1/0 STORE:1/0 REF:1/0 PLUSPER:1/0
# LOBAT:1/0
###### MAIN DISPLAY
# AC1:1/0 SIGN1:1/0 DC1:1/0 PW1:1/0
# MD5:np MD4:np MD3:np MD2:np MD1:n
# M1:1/0 k1:1/0 OHM1:1/0
# u1:1/0 Hz1:1/0
# m1:1/0 V1:1/0 A1:1/0
# n1:1/0 S1:1/0 F1:1/0
# DEGC1:1/0 s1:1/0
# DEGF1:1/0
###### BAR GRAPH
# B16K:1/0 B8K:1/0 B4K:1/0 B2K:1/0 B1K:1/0
# B512:1/0 B256:1/0 B128:1/0 B64:1/0 B32:1/0
# B16:1/0 B8:1/0 B4:1/0 B2:1/0 B1:1/0 B0:1/0
###### SUB DISPLAY
# RANGE2:1/0 HOLD2:1/0 DUTY2:1/0
# CONT2:1/0 ZD2:1/0
# AC2:1/0 SIGN2:1/0 DC2:1/0
# SD5:np SD4:np SD3:np SD2:np SD1:n
# PERCENT2:1/0 dBm2:1/0
# m2:1/0 V2:1/0 A2:1/0 DEGK2:1/0
# G2:1/0 M2:1/0 k2:1/0 OHM2:1/0 Hz2:1/0
# ROTSWITCH:[0-8]
### The return values for nine bytes is undocumented,
### they are labelled as X1 to X9
# X[1-9]:?
##
# @brief Sends a state report request to the protek script context protocol handler.
proc rqStateRep {} {
sct send "STATE"
return rdStateRep
}
##
# @brief Processes state report from the protek script context protocol handler.
proc rdStateRep {} {
set stateRep [sct result]
if {[string match "ASCERR:*" $stateRep]} {
sct geterror $stateRep
# Setting oldval forces update and clears geterror on next call
sct oldval "UNKNOWN"
return idle
}
array set stateArr [split $stateRep "|:"]
if {$stateArr(AUTOOFF)} {
broadcast "PROTEK608:[sct IP]:[sct PORT] WARNING AUTO OFF IS ENABLED"
}
if {$stateArr(LOBAT)} {
broadcast "PROTEK608: LOW BATTERY WARNING"
}
if {$stateRep != [sct oldval]} {
sct update $stateRep
sct oldval $stateRep
}
return idle
}
proc rqVal {nextState} {
return $nextState
}
proc ProtekMainDisplay {protek nextState} {
if [catch {
set stateRep [hval $protek/state]
set MDpath [sct]
set parname [file tail $MDpath]
set scale [hval $MDpath/scale]
set offset [hval $MDpath/offset]
array set SA [split $stateRep "|:"]
if {$SA(SIGN1)} {
set sign1 "-"
} else {
set sign1 ""
}
set MDISP "$sign1$SA(MD5)$SA(MD4)$SA(MD3)$SA(MD2)$SA(MD1)"
if {[string is double $MDISP] == 0} {
error "Non-numeric reading ($MDISP) from Protek main display"
}
set MDISP [expr {$scale * $MDISP + $offset}]
set oldval [sct oldval]
if {$MDISP != $oldval} {
sct update $MDISP
sct oldval $MDISP
protek_debug_log $parname $MDISP
}
} msg ] {
return -code error "[info level 0]: $msg"
}
return $nextState
}
proc protek_debug_log {name args} {
set fd [open "/usr/local/sics/data/protek_$name.csv" a]
puts $fd "[clock format [clock seconds] -format "%d/%m/%Y, %T"], $args"
close $fd
}
##
# @brief Makes a state monitor object for the protek multimeter
#
# @param name, name of object which reports main display reading
# @param IP, IP address of protek moxa box
# @param PORT, port number for protek on moxa box
# @param scale, scales reading to physical units
# @param offset, offset for main display reading
# @param interval, polling interval in seconds (optional, default = 0.5seconds)
# @param cbFunc, this function will be called after the voltage reading has been updated
# NOTE: If the interval is negative then the multimeter will be polled on every cycle of
# the SICS task loop.
proc MakeProtek {name IP PORT {scale 1.0} {offset 0.0} {interval 0.5} {cbFunc "return idle"}} {
set catch_status [ catch {
set sctName "sct_$name"
set sobjName "$name"
set soState "so_state_$name"
clientput "MakeSICSObj $sobjName SCT_OBJECT"
MakeSICSObj $soState SCT_OBJECT
MakeSICSObj $sobjName SCT_OBJECT user float
sicslist setatt $sobjName long_name $sobjName
hfactory /sics/$soState/state plain user text
hsetprop /sics/$soState/state read rqStateRep
hsetprop /sics/$soState/state rdStateRep rdStateRep
hsetprop /sics/$soState/state oldval "UNKNOWN"
hsetprop /sics/$sobjName read rqVal "reportVal"
hsetprop /sics/$sobjName reportVal ProtekMainDisplay /sics/$soState callBack
hsetprop /sics/$sobjName callBack $cbFunc
hfactory /sics/$sobjName/scale plain user float
hset /sics/$sobjName/scale $scale
hfactory /sics/$sobjName/offset plain user float
hset /sics/$sobjName/offset $offset
hsetprop /sics/$sobjName oldval "UNKNOWN"
sicslist setatt $sobjName klass sample
::scobj::hinitprops $sobjName
sicslist setatt $sobjName long_name $name
if {[SplitReply [environment_simulation]] == false} {
makesctcontroller $sctName protek608 $IP:$PORT
hsetprop /sics/$soState/state IP $IP
hsetprop /sics/$soState/state PORT $PORT
$sctName poll /sics/$soState/state $interval
$sctName poll /sics/$sobjName $interval
}
} catch_message ] {
handle_exception ${catch_status} ${catch_message}
}
return /sics/$sobjName
}
proc add_protekmm {name IP PORT CTYPE CID {scale 1.0} {offset 0.0} {interval 0.5} {cbFunc "return idle"}} {
set catch_status [ catch {
MakeProtek $name $IP $PORT $scale $offset $interval $cbFunc
hsetprop /sics/$name permlink data_set ${CTYPE}${CID}S1
} catch_message ] {
handle_exception ${catch_status} ${catch_message}
}
}
namespace eval ::scobj::add_protekmm {
set debug_threshold 5
}
proc ::scobj::add_protekmm::sics_log {debug_level debug_string} {
set catch_status [ catch {
set debug_threshold ${::scobj::add_protekmm::debug_threshold}
if {${debug_level} >= ${debug_threshold}} {
sicslog "::scobj::add_protekmm::${debug_string}"
}
} catch_message ]
}
clientput "file evaluation of sct_protekmm.tcl"
::scobj::add_protekmm::sics_log 9 "file evaluation of sct_protekmm.tcl"
proc ::scobj::add_protekmm::read_config {} {
set catch_status [ catch {
set ns "::scobj::add_protekmm"
dict for {k v} $::config_dict {
if { [dict exists $v "implementation"] } {
if { !([dict exists $v "name"] && [dict exists $v "enabled"]) } {
continue
}
set name [dict get $v name]
set enabled [string tolower [dict get $v "enabled"]]
set implementation [dict get $v "implementation"]
if { !([dict exists $::config_dict $implementation]) } {
continue
}
set v [dict get $::config_dict $implementation]
if { !([dict exists $v "driver"]) } {
continue
}
if { [string equal -nocase [dict get $v "driver"] "protekmm"] } {
if { [string equal -nocase $enabled "true" ] || [string equal -nocase $enabled "always"] } {
if { ![string equal -nocase [SplitReply [environment_simulation]] "false"] } {
set asyncqueue "null"
${ns}::sics_log 9 "[environment_simulation] => using null asyncqueue"
} elseif { [dict exists $v "asyncqueue"] } {
set asyncqueue [dict get $v "asyncqueue"]
} else {
if { [dict exists $v "asyncprotocol"] } {
set asyncprotocol [dict get $v "asyncprotocol"]
} else {
set asyncprotocol ${name}_protocol
MakeAsyncProtocol ${asyncprotocol}
if { [dict exists $v "terminator"] } {
${asyncprotocol} sendterminator "[dict get $v "terminator"]"
${asyncprotocol} replyterminator "[dict get $v "terminator"]"
}
}
set asyncqueue ${name}_queue
set IP [dict get $v ip]
set PORT [dict get $v port]
MakeAsyncQueue ${asyncqueue} ${asyncprotocol} ${IP} ${PORT}
if { [dict exists $v "timeout"] } {
${asyncqueue} timeout "[dict get $v "timeout"]"
}
}
set arg_list [list]
foreach arg {name ip port datype id scale offset interval} {
if {[dict exists $v $arg]} {
lappend arg_list "[dict get $v $arg]"
} else {
${ns}::sics_log 9 "Missing configuration value $arg"
error "Missing configuration value $arg"
}
}
add_protekmm ${name} "aqadapter" ${asyncqueue} {*}$arg_list
}
}
}
}
} catch_message ]
handle_exception ${catch_status} ${catch_message}
}
if { [info exists ::config_dict] } {
::scobj::add_protekmm::read_config
} else {
::scobj::add_protekmm::sics_log 5 "No config dict"
}