esrf

Beamline Instrument Software Support
SPEC Macro documentation: [ Macro Index | BCU Home ]

#%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%