#%TITLE% ICE.MAC
#
#%NAME%
# Implement VAISALA PTU300 meteo controller readout through macro counters.
# WARNING: the controller accepts only one socket connection at a time
#
#%CATEGORY% Temperature
#
#%DESCRIPTION%
# Configure a "Macro Counter" scaler type with device field set to "ptu300"
# and the address field to the hostname of the VAISALA controller.
#
# Configure 3 counters with channel field set to:
#%DL%
# %DT% 1 for pression values
# %DT% 2 for temperature values
# %DT% 3 for relative humidity values
#%XDL%
#
global PTU300_CNT[]
global PTU300_IDX[]
PTU300_IDX[1] = "P 10 s intervals"
PTU300_IDX[2] = "P 90 s intervals"
PTU300_IDX[3] = "P 12 min intervals"
PTU300_IDX[4] = "P 2 h intervals"
PTU300_IDX[5] = "P 12 h intervals"
PTU300_IDX[6] = "P 3 d intervals"
PTU300_IDX[7] = "T 10 s intervals"
PTU300_IDX[8] = "T 90 s intervals"
PTU300_IDX[9] = "T 12 min intervals"
PTU300_IDX[10] = "T 2 h intervals"
PTU300_IDX[11] = "T 12 h intervals"
PTU300_IDX[12] = "T 3 d intervals"
PTU300_IDX[13] = "RH 10 s intervals"
PTU300_IDX[14] = "RH 90 s intervals"
PTU300_IDX[15] = "RH 12 min intervals"
PTU300_IDX[16] = "RH 2 h intervals"
PTU300_IDX[17] = "RH 12 h intervals"
PTU300_IDX[18] = "RH 3 d intervals"
#%UU% [hostname [index [filename]]]
#%MDESC%
# Interactive macro to upload from the controller a recorded bunch
# of data and store it into a five file.
#
def ptu300_dump '{
local dev, idx, fname
local i
# Get controller to talk to
if($# > 0) {
dev = "$1"
} else {
dev = getval("Name of the VAISALA PTU300 controller", "meteopel")
}
# Which predefined embedded record has to be dumped
if($# > 1) {
idx = $2
} else {
for(i in PTU300_IDX) {
printf("\t%2d\t%s\n", i, PTU300_IDX[i])
}
idx = getval("Index of embedded record to dump", 7)
}
# Minimum check on index
if(PTU300_IDX[idx] == 0) {
_icepap_err
print "wrong embedded record index"
return(-1)
}
if($# > 2) {
fname = "$3"
} else {
fname = _ptu800_fname(idx)
fname = getval("File name", fname)
}
_ptu300_dump(dev, idx, fname)
}'
#%UU% [hostname [directory]]
#%MDESC%
# Interactive macro to upload from the controller all recorded bunch
# of data and store them into files.
#
def ptu300_dumpall '{
local dev, dir
local i
# Get controller to talk to
if($# > 0) {
dev = "$1"
} else {
dev = getval("Name of the VAISALA PTU300 controller", "meteopel")
}
# Get a directory to receive all the files at once
if($# > 1) {
dir = "$2"
} else {
dir = getval("Destination directory", "/tmp")
}
# Shoot, dont talk
for(i in PTU300_IDX) {
_ptu300_dump(dev, i, _ptu800_fname(i, dir))
}
}'
#%IU%(idx, directory)
#%MDESC%
# Returns a useful file name from given index of the embedded record.
# The directory is optional.
#
def _ptu800_fname(idx, dir) '{
local path
path = sprintf("%s", (dir != 0)?dir:".")
return sprintf("%s/%s_%s.txt", \
path, \
date("%y%m%d_%H%M%S"), \
_ptu300_sp2us(PTU300_IDX[idx]))
}'
#%IU%(dev, idx, file)
#%MDESC%
# Upload from the controller a recorded bunch
# of data and store it into a five file.
# Returns non null if an error occured.
#
def _ptu300_dump(dev, idx, fname) '{
global PTU300_IDX[]
# Minimum check on index
if(PTU300_IDX[idx] == 0) {
_icepap_err
print "wrong embedded record index"
return(-1)
}
on(fname);p _ptu300_query(dev, sprintf("PLAY %d", idx)); off(fname)
close(fname)
return(0)
}'
#%IU%
#%MDESC%
# Returns the given string with all spaces replaced by underscores
#
def _ptu300_sp2us(str) '{
local ret
local i, l, lc
ret = str
len = length(ret)
for(i=1; i<len; i++) {
if(substr(ret, i, 1) == " ") {
ret = substr(ret, 0, i-1) "_" substr(ret, i+1)
}
}
return ret
}'
#%IU%
#%MDESC%
# MACRO COUNTER:
# Called by spec after reading the config file
#
def ptu300_config(num,type,p1,p2,p3) '{
global PTU300_CNT[]
local silent
local mne
local dev
local i
silent = 1
# p1 is the controller number
# p2 is the number of channels
if(type=="ctrl") {
# Erase any previous configuration
if (p1 == 0) { for(i in PTU300_CNT) { delete PTU300_CNT[i] } }
# Get controller hostname
dev=ptu300_ADDR
# Check that the controller is usable
ans = _ptu300_query(dev, "VERS", silent)
if(ans == "") {
_ptu300_err
print "PTU300 \""dev"\" not reachable"
return ".error."
}
# Force command echoing to be compatible with our parser
_ptu300_query(dev, "ECHO ON", silent)
# Normal end, the controller is usable
print "Using meteo controller VAISALA PTU300: \""dev"\""
PTU300_CNT[p1]["dev"] = dev
return
}
# p1 is the unit
# p2 is always 0
# p3 is the channel which is the two digits address
if(type=="cnt") {
mne=cnt_mne(num)
# Get physical value to be read
if(p3 == 1) {
typ = "pression"
} else if(p3 == 2) {
typ = "temperature"
} else if(p3 == 3) {
typ = "relhumidity"
} else {
_ptu300_err
print "invalid channel for counter \""mne"\" (must be 1,2,3)"
return ".error."
}
PTU300_CNT[mne]["typ"] = typ
# Get controller hostname
PTU300_CNT[mne]["dev"] = ""
dev=counter_par(num,"address")
PTU300_CNT[mne]["dev"] = dev
# Cross reference
PTU300_CNT[p1]["cnts"] = sprintf("%s%s ", PTU300_CNT[p1]["cnts"], mne)
PTU300_CNT[mne]["unit"] = p1
}
}'
#%IU%
#%MDESC%
# MACRO COUNTER:
# readout of the controller
#
def ptu300_cmd(num,key,p1,p2,p3) '{
global PTU300_CNT[]
local mne
local dev
local i
# p3 is the controller number
if (key == "prestart_all") {
# Erase any previous readout
PTU300_CNT[p3]["ans"] = ""
}
if (key == "counts") {
mne=cnt_mne(num)
# Get controller hostname
unit=PTU300_CNT[mne]["unit"]
# Update controller readout
if(PTU300_CNT[unit]["ans"] == "") {
_ptu300_updatevalues(unit)
}
S[num]=PTU300_CNT[mne]["val"]
}
}'
#%IU%(dev, cmd, silent)
#%MDESC%
#
def _ptu300_updatevalues(unit) '{
global PTU300_CNT[]
local i, n
local dev
local ans
local cnts[]
local vals[]
local val_t, val_p, val_h
# Erase previous values
split(PTU300_CNT[unit]["cnts"], cnts)
for(i in cnts) {
PTU300_CNT[cnts[i]]["val"] = 0
}
# Get all values at once
dev = PTU300_CNT[unit]["dev"]
if((ans = _ptu300_query(dev, "SEND")) == "") {
return(-1)
}
# Minimum chekc on answer
if(split(ans, vals, "=") != 4) {
_ptu300_err
print "wrong raw data from controller"
return(-1)
}
PTU300_CNT[p3]["ans"] = ans
# Parse the values
if(sscanf(vals[1],"%f", val_p) != 1) {
_ptu300_err
print "unable to parse pression value"
return(-1)
}
if(sscanf(vals[2],"%f", val_t) != 1) {
_ptu300_err
print "unable to parse temperature value"
return(-1)
}
if(sscanf(vals[3],"%f", val_h) != 1) {
_ptu300_err
print "unable to parse humidity value"
return(-1)
}
# For debug only
#p val_p, val_t, val_h
# Update counters values
for(i in cnts) {
mne = cnts[i]
if(PTU300_CNT[mne]["typ"] == "pression") {
PTU300_CNT[mne]["val"] = val_p
}
if(PTU300_CNT[mne]["typ"] == "temperature") {
PTU300_CNT[mne]["val"] = val_t
}
if(PTU300_CNT[mne]["typ"] == "relhumidity") {
PTU300_CNT[mne]["val"] = val_h
}
}
# Normal end
return(0)
}'
#%IU%(dev, cmd, silent)
#%MDESC%
#
def _ptu300_query(dev, cmd, silent) '{
local ans
# Emulate the telnet connection
if(!index(dev,":")) { dev = dev":23" }
# Not documented but mandatory
if(substr(cmd, length(cmd)) != "\r") { cmd = cmd"\r" }
# Send first an CR to get out of any pending interactive command
sock_put(dev, "\r")
ans = sock_get(dev, ">")
# Handle timeout
if((ans == "") && !silent) {
_ptu300_err
print "timeout waiting for answer"
return ""
}
# Just in case
sock_par(dev, "flush")
# Talk to the controller
sock_put(dev, cmd)
# Use the interactive prompt as answer terminator
ans = sock_get(dev, ">")
# Handle timeout
if((ans == "") && !silent) {
_ptu300_err
print "timeout waiting for answer"
return ""
}
# By default the command sent is echoed, remove it
ans = substr(ans, index(ans, "\r\n")+2)
# Check that the echo
# LAZY
# Remove also the prompt
ans = substr(ans, 0, length(ans)-1)
# Remove also CR LF if any
for(;;) {
lc = substr(ans, length(ans))
if((lc != "\n") && (lc != "\r")) {
break
}
ans = substr(ans, 0, length(ans)-1)
}
# No answers means probably a wrong question
if((ans == "") && !silent) {
_ptu300_err
print "invalid command"
return ""
}
# Normal end
return ans
}'
#%IU%
#%MDESC%
#
def _ptu300_err '{
tty_cntl("md")
printf("PTU300 ERROR: ")
tty_cntl("me")
}'
#%MACROS%
#%IMACROS%
#%AUTHOR% MP BLISS (Original 4/2014).
# %BR%$Revision: 1.0 $ / $Date: 2014/04/14 13:39:08 $
#%TOC%
|