#
# $Log: not supported by cvs2svn $
# Revision 1.59  2011/12/16 08:35:26  soliday
# The RunControlPV variable has different upper and lower case in different
# parts of the code. My previous change didn't account for that.
#
# Revision 1.58  2011/12/15 17:37:55  soliday
# Updated the data logger cron job to fix a problem with looking for non-existant
# PVs that was pointed out to me by Marty Smith.
#
# Revision 1.57  2011/01/18 17:09:47  soliday
# Updated email addresses.
#
# Revision 1.56  2010/05/27 17:49:32  soliday
# Updated to fix issue on auric because the hostname includes .aps.anl.gov
#
# Revision 1.55  2010/01/19 21:06:10  soliday
# Updated to convert the returned hostname so that it only looks at the portion
# before the first period.
#
# Revision 1.54  2007/05/11 19:10:54  soliday
# Prior to starting a data logger it checks to see if the runcontrol is active.
# If it is, it skips the data logger. If it is not then it clears the runcontrol
# prior to starting a new datalogger. I fixed a problem so that it does not
# attempt to clear the runcontrol if it fails to connect to it in the previous
# step. Runcontrol were being cleared while they were really active. The data
# logger would then continue to run but the runcontrol would not display this.
# Pinging the runcontrol would still work because no timeout was shown.
#
# Revision 1.53  2006/07/06 16:45:50  soliday
# Fixed a typo.
#
# Revision 1.52  2006/07/06 16:43:07  soliday
# Updated a variable name.
#
# Revision 1.51  2006/07/06 16:28:44  soliday
# Updated to work with run controls for sddsmonitor, sddslogonchange, sddsalarmlog
# and sddsglitchlogger.
#
# Revision 1.50  2006/06/07 21:20:05  soliday
# Increased the Run Control timeout from the default.
#
# Revision 1.49  2006/06/07 19:46:01  soliday
# Updated to use Run Control for sddslogger.
#
# Revision 1.48  2006/06/07 15:49:41  soliday
# Removed code used to set the EPICS_CA_ADDR_LIST env variable because
# it is no longer needed and may never have been needed.
#
# Revision 1.47  2004/12/01 19:16:51  soliday
# Added the ability start an sddslogger with a specific EPICS_CA_ADDR_LIST value.
#
# Revision 1.46  2004/02/19 16:15:53  soliday
# Removed special code for FloorCoord data logger that is no longer needed.
#
# Revision 1.45  2004/01/19 19:25:38  soliday
# Modified to work with the FloorCoord logonchange logger that connects to an IOC
# which runs on a workstation which means that the EPICS related
# environment variables had to be set prior to launching the logger and unset
# before launching other loggers.
#
# Revision 1.44  2003/11/05 18:37:37  borland
# Added time-stamp for sddslogger check/restart messages.
# Increased running time for sddsglitchlogger jobs from 7 to 365 days to
# prevent weekly restarts at midnight.
#
# Revision 1.43  2002/03/31 20:34:19  borland
# Removed verbose qualifier from -dailyFiles option for sddslogger as
# it was removed from the program.
#
# Revision 1.42  2002/03/28 22:28:29  soliday
# I had problems with the last cvs operation.
#
# Revision 1.41  2002/03/28 22:27:54  soliday
# Changed the extension again.
#
# Revision 1.40  2002/03/28 22:19:07  soliday
# Changed the input extension for sddslogonchange
#
# Revision 1.39  2002/03/28 22:03:29  soliday
# Added support for sddslogonchange.
#
# Revision 1.38  2001/11/16 14:12:14  borland
# sddslogger jobs now get the verbose qualifier for -dailyFiles.
#
# Revision 1.37  2001/11/13 06:22:47  borland
# Reduced the overlap time for sddsmonitor-like jobs from 6-12 minutes to
# 1 minute.
#
# Revision 1.36  2001/09/25 20:16:59  borland
# Another change to accomodate sddsglitchlogger (input file extension is .atrig
# instead of .mon).
#
# Revision 1.35  2001/09/25 20:12:33  borland
# More changes to accomodate sddsglitchlogger.
#
# Revision 1.34  2001/09/25 20:04:13  borland
# No longer tries to pass -dailyFiles option to sddsglitchlogger.
#
# Revision 1.33  2001/02/20 15:51:29  borland
# Improved error messages from scalar monitor postprocessing.  Added
# postprocessingWorkstation field to time series logger config.
#
# Revision 1.32  2000/12/07 02:23:02  borland
# Fixed problem with previous change.
#
# Revision 1.31  2000/12/07 02:16:43  borland
# Fixed sddslogger launch problem (extra arguments were left out).
#
# Revision 1.30  2000/11/27 19:44:40  borland
# Added support for multiple sddslogger jobs on one workstation.
#
# Revision 1.29  2000/03/18 15:37:21  borland
# Now sends normal email notifcatin of data logger startup only if the
# hour is not close to midnight.
#
# Revision 1.28  1999/11/14 03:05:16  borland
# Another change to clean up tmp files (for sddslogger jobs).
#
# Revision 1.27  1999/11/11 15:54:20  borland
# Now uses extraArguments column for sddslogger jobs.
#
# Revision 1.26  1999/11/11 14:55:59  borland
# Added -inhibitPV option for sddslogger.  Increased pend IO time for this
# option on sddsmonitor jobs to 120s.
#
# Revision 1.25  1999/11/05 14:40:52  borland
# The default pendIOTime for sddslogger jobs is now 30s.
#
# Revision 1.24  1999/10/08 20:21:25  borland
# Added -inhibitPV option for data loggers as the default.
#
# Revision 1.23  1999/08/26 00:56:50  borland
# Removed -inhibitPV argument as it caused problems with some loggers.
#
# Revision 1.22  1999/08/24 13:44:39  borland
# Added a debuging statement.
#
# Revision 1.21  1999/08/23 14:52:31  borland
# Added -inhibitPV=DataLoggersOnOffBO option to monitor jobs.
# Allows suspension of PV polling using a single PV control.
#
# Revision 1.20  1999/07/14 17:46:44  borland
# Increased the running time for sddslogger to 365 days.
#
# Revision 1.19  1999/07/13 19:09:04  borland
# Integrated startup of sddslogger jobs with traditional data loggers, using
# a single configuration file and startup script.
#
# Revision 1.18  1999/07/13 17:54:13  borland
# First version of APSStartSddsloggers, which starts sddslogger job for a
# workstation.
#
# Revision 1.17  1999/06/05 14:12:12  borland
# More corections for -dailyFiles mode.
#
# Revision 1.16  1999/06/05 14:03:24  borland
# For starting time series loggers in dailyFiles mode, the time of the
# run is no longer limited to 26hours.
#
# Revision 1.15  1999/06/01 14:21:59  borland
# Added -time option for -dailyFiles mode, which was omitted by mistake in last
# revision.
#
# Revision 1.14  1999/05/26 16:16:04  borland
# Now supports use of other loggers (like sddslogger) that have the -dailyFiles
# option rather than -generations.
#
# Revision 1.13  1999/04/30 22:00:22  borland
# Now reads and uses the intervalOptionName column from the config file.
#
# Revision 1.12  1998/11/16 14:47:34  borland
# Now actually uses the "email on" variables.
#
# Revision 1.11  1997/10/31 08:08:15  borland
# Now read and define globalProcessingScript from config file.
#
# Revision 1.10  1997/07/22 15:45:21  borland
# Reduced the sleep time after launching a new job, so that scripts can
# start more jobs sequentially in less time.
#
# Revision 1.9  1997/06/03 18:54:33  borland
# Added procedures to implement file expiration.
#
# Revision 1.8  1997/05/30 20:12:30  borland
# Added check on total time computed for data logging.  Don't let it
# exceed 26 hours.
#
# Revision 1.7  1997/05/20 19:25:36  borland
# Modified to support -nextDay option, for starting loggers just
# before midnight.  Also modified the length of time the jobs run
# to span from about 5 mins before midnight to 5 mins after the
# next day.
#
# Revision 1.6  1996/12/13 22:57:51  borland
# Removes temporary files now.
#
# Revision 1.5  1996/11/24 17:55:55  borland
# Improved error return messages from APSReadTimeSeriesLoggerConfig.
#
# Revision 1.4  1996/11/22 01:55:09  borland
# Changed names of data logging and configuration directory variables, plus
# variable values, in order to make addition of glitch loggers easier.
#
# Revision 1.3  1996/11/22 01:21:38  borland
# Changed the monitoring setup directory.
#
# Revision 1.2  1996/11/21 15:44:35  borland
# Renamed some routines with proper APS names.  Added implementation
# of keepGenerations flag for post-processing.
#
# Revision 1.1  1996/11/21 15:14:16  borland
# First version of scripts and procedures to manage time-series data logging
# cron jobs.
#
#

# Defines some global variables.
proc APSDefineMonitoringVariables {} {
    global apsOAGDataDir apsDataLoggerConfigDir 
    global apsDataManagerNormalEmail apsDataManagerPagerEmail 
    global apsDataManagerNormalEmailOn apsDataManagerPagerEmailOn
    set apsOAGDataDir   /home/helios/oagData
    set apsDataLoggerConfigDir  /home/helios/oagData/dataLoggerConfig

#    set apsDataManagerNormalEmail   borland@aps.anl.gov
#    set apsDataManagerPagerEmail    4205@aps.anl.gov
    set apsDataManagerNormalEmail   soliday@aps.anl.gov
    set apsDataManagerPagerEmail    soliday@aps.anl.gov
    set apsDataManagerNormalEmailOn 1
    set apsDataManagerPagerEmailOn  1
    # this script may override the previous 4 lines
    source $apsDataLoggerConfigDir/emailSetup.tcl
}

# Send an email message to both data manager addresses,
# regardless of whether email is enabled or not.
proc APSSendDataLoggerErrorEmail {args} {
    global apsDataManagerNormalEmailOn apsDataManagerPagerEmailOn
    global apsDataManagerNormalEmail apsDataManagerPagerEmail
    set message ""
    APSStrictParseArguments {message mailProgram}
    if {$apsDataManagerNormalEmailOn && [string length $apsDataManagerNormalEmail]} {
        APSSendEMail \
          -address $apsDataManagerNormalEmail -message $message
    }
    if {$apsDataManagerPagerEmailOn && [string length $apsDataManagerPagerEmail]} {
        APSSendEMail \
          -address $apsDataManagerPagerEmail -message $message
    }
}

# starts sddslogger job using a configuration file 

proc APSStartSddslogger {args} {
    APSDefineMonitoringVariables
    global apsOAGDataDir apsScriptHost
    global apsDataManagerNormalEmail apsDataManagerPagerEmail
    global apsDataManagerNormalEmailOn apsDataManagerPagerEmailOn env

    set configFile ""
    APSStrictParseArguments {configFile}
    if ![file exists $configFile] {
        return -code error "not found: $configFile (APSStartSddslogger)"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    set workstation [lindex [split $apsScriptHost .] 0]
    if [catch {exec sddsprocess $configFile -pipe=out -nowarning \
                 -match=column,monitorProgram=sddslogger \
                 -match=column,workstation=$workstation \
                 | sddssort -pipe -column=loggerGroup \
                 | sddsbreak -pipe -changeOf=loggerGroup \
                 | sddsprocess -pipe \
                 -process=workstation,first,workstation \
                 -process=sampleInterval,min,sampleInterval \
                 -process=loggerGroup,first,loggerGroup \
                 -process=RunControlPV,first,RunControlPV \
                 | sddsconvert -pipe \
                 -delete=column,sampleInterval,workstation,loggerGroup,RunControlPV \
                 | tee $tmpFile \
                 | sddscombine -pipe -merge \
                 | sdds2stream -rows -pipe} result] {
        return -code error "unable to prepare $configFile: $result (APSStartSddslogger)"
    }
    if [string compare $result "0 rows"]==0 {
        return
    }
    
    if [catch {sdds load $tmpFile configData} result] {
        return -code error "unable to load $tmpFile: $result (APSStartSddslogger)"
    }
    set oldDir [pwd]
    cd $apsOAGDataDir
    set page -1
    foreach loggerGroup $configData(Parameter.loggerGroup) {
        incr page
        set sampleInterval [lindex $configData(Parameter.sampleInterval) $page]
        set runControlPV [lindex $configData(Parameter.RunControlPV) $page]
        foreach column [list rootname subDirectory doRun extraArguments] {
            set ${column}List [lindex $configData(Column.$column) $page] 
        }
        set argList ""
        set argList1 ""
        foreach rootname $rootnameList subDirectory $subDirectoryList \
          doRun $doRunList extraArguments $extraArgumentsList {
              if !$doRun continue
              lappend argList $subDirectory/$rootname.mon 
              lappend argList $subDirectory/$rootname
              foreach extraArg [split [string trim $extraArguments]] {
                  lappend argList1 $extraArg
              }
          }
        set rcArguments ""
        if {([llength $runControlPV]) && ([string range $runControlPV 0 4] != "Dummy")} {
            global ${runControlPV}.RUN ${runControlPV}.CLR
            if {[pv linkw ${runControlPV}.RUN ${runControlPV}.RUN] == 0} {
                if {[set ${runControlPV}.RUN] == 1} {
                    continue
                }
                if {[pv linkw ${runControlPV}.CLR ${runControlPV}.CLR] == 0} {
                    set ${runControlPV}.CLR 1
                    pv putw ${runControlPV}.CLR
                }
            }
            set rcArguments "-runControlPV=string=$runControlPV,pingTimeout=80 \"-runControlDescription=string=$loggerGroup sddslogger\""
        }
        if [llength $argList] {
            if [file exists logging/$workstation.logfile$loggerGroup] {
                APSLimitFileSize -fileName logging/$workstation.logfile$loggerGroup -lineLimit 500
            }
            catch {APSAddToMonitoringLogFile -fileName logging/$workstation.logfile$loggerGroup \
                     -text "Checking/starting job"}  
            eval exec echo sddslogger $argList $argList1 $rcArguments -pendIOTime=30 \
              -sampleInterval=$sampleInterval,s -dailyFiles -time=365,days -enforceTimeLimit \
              -inhibitPV=name=DataLoggersOnOffBO,pend=120,wait=5 \
              >>& logging/$workstation.logfile$loggerGroup
            if {$loggerGroup == "DMM"} {
                global env
                set env(EPICS_CA_ADDR_LIST) 164.54.8.48
            }
            if {0} {
                eval exec sddslogger $argList $argList1 $rcArguments -pendIOTime=30 \
                  -sampleInterval=$sampleInterval,s -dailyFiles -time=365,days -enforceTimeLimit \
                  -inhibitPV=name=DataLoggersOnOffBO,pend=120,wait=5 \
                  >>& logging/$workstation.logfile$loggerGroup &
            } else {
                eval exec sddslogger $argList $argList1 $rcArguments -pendIOTime=30 \
                  -sampleInterval=$sampleInterval,s -dailyFiles -time=365,days -enforceTimeLimit \
                  >>& logging/$workstation.logfile$loggerGroup &
            }
            if {$loggerGroup == "DMM"} {
                global env
                unset env(EPICS_CA_ADDR_LIST)
            }
        }
    }
}

# starts sddslogger job using a configuration file 

proc APSStartSddspvalogger {args} {
    APSDefineMonitoringVariables
    global apsOAGDataDir apsScriptHost
    global apsDataManagerNormalEmail apsDataManagerPagerEmail
    global apsDataManagerNormalEmailOn apsDataManagerPagerEmailOn env

    set configFile ""
    APSStrictParseArguments {configFile}
    if ![file exists $configFile] {
        return -code error "not found: $configFile (APSStartSddspvalogger)"
    }
    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    set workstation [lindex [split $apsScriptHost .] 0]
    if [catch {exec sddsprocess $configFile -pipe=out -nowarning \
                 -match=column,monitorProgram=sddspvalogger \
                 -match=column,workstation=$workstation \
                 | sddssort -pipe -column=loggerGroup \
                 | sddsbreak -pipe -changeOf=loggerGroup \
                 | sddsprocess -pipe \
                 -process=workstation,first,workstation \
                 -process=sampleInterval,min,sampleInterval \
                 -process=loggerGroup,first,loggerGroup \
                 -process=RunControlPV,first,RunControlPV \
                 | sddsconvert -pipe \
                 -delete=column,sampleInterval,workstation,loggerGroup,RunControlPV \
                 | tee $tmpFile \
                 | sddscombine -pipe -merge \
                 | sdds2stream -rows -pipe} result] {
        return -code error "unable to prepare $configFile: $result (APSStartSddspvalogger)"
    }
    if [string compare $result "0 rows"]==0 {
        return
    }
    
    if [catch {sdds load $tmpFile configData} result] {
        return -code error "unable to load $tmpFile: $result (APSStartSddspvalogger)"
    }
    set oldDir [pwd]
    cd $apsOAGDataDir
    set page -1
    foreach loggerGroup $configData(Parameter.loggerGroup) {
        incr page
        set sampleInterval [lindex $configData(Parameter.sampleInterval) $page]
        set runControlPV [lindex $configData(Parameter.RunControlPV) $page]
        foreach column [list rootname subDirectory doRun extraArguments] {
            set ${column}List [lindex $configData(Column.$column) $page] 
        }
        set argList ""
        set argList1 ""
        foreach rootname $rootnameList subDirectory $subDirectoryList \
          doRun $doRunList extraArguments $extraArgumentsList {
              if !$doRun continue
              lappend argList $subDirectory/$rootname.mon 
              lappend argList $subDirectory/$rootname
              foreach extraArg [split [string trim $extraArguments]] {
                  lappend argList1 $extraArg
              }
          }
        set rcArguments ""
        if {([llength $runControlPV]) && ([string range $runControlPV 0 4] != "Dummy")} {
            global ${runControlPV}.RUN ${runControlPV}.CLR
            if {[pv linkw ${runControlPV}.RUN ${runControlPV}.RUN] == 0} {
                if {[set ${runControlPV}.RUN] == 1} {
                    continue
                }
                if {[pv linkw ${runControlPV}.CLR ${runControlPV}.CLR] == 0} {
                    set ${runControlPV}.CLR 1
                    pv putw ${runControlPV}.CLR
                }
            }
            set rcArguments "-runControlPV=string=$runControlPV,pingTimeout=80 \"-runControlDescription=string=$loggerGroup sddspvalogger\""
        }
        if [llength $argList] {
            if {$sampleInterval < .00000001} {
                set si ""
            } else {
                set si -sampleInterval=$sampleInterval,s
            }
            if [file exists logging/$workstation.logfile$loggerGroup] {
                APSLimitFileSize -fileName logging/$workstation.logfile$loggerGroup -lineLimit 500
            }
            catch {APSAddToMonitoringLogFile -fileName logging/$workstation.logfile$loggerGroup \
                     -text "Checking/starting job"}
            if {$workstation == "head3"} {
                eval exec echo sddspvalogger $argList $argList1 $rcArguments -pendIOTime=30 \
                    $si -dailyFiles -time=365,days \
                    >>& logging/$workstation.logfile$loggerGroup
                eval exec sddspvalogger $argList $argList1 $rcArguments -pendIOTime=30 \
                    $si -dailyFiles -time=365,days \
                    >>& logging/$workstation.logfile$loggerGroup &
            } else {
                eval exec echo sddspvalogger $argList $argList1 $rcArguments -pendIOTime=30 \
                    $si -dailyFiles -time=365,days \
                    -inhibitPV=name=DataLoggersOnOffBO,provider=ca,wait=5 \
                    >>& logging/$workstation.logfile$loggerGroup
                if {0} {
                    eval exec sddspvalogger $argList $argList1 $rcArguments -pendIOTime=30 \
                      $si -dailyFiles -time=365,days \
                      -inhibitPV=name=DataLoggersOnOffBO,provider=ca,wait=5 \
                      >>& logging/$workstation.logfile$loggerGroup &
                } else {
                    eval exec sddspvalogger $argList $argList1 $rcArguments -pendIOTime=30 \
                      $si -dailyFiles -time=365,days \
                      >>& logging/$workstation.logfile$loggerGroup &
                }
            }
        }
    }
}

# Starts an sddsmonitor-like job for 24 hours or the remainder of the
# day.  Specifically for use by the OAG data logger.
proc APSStartTimeSeriesLogger {args} {
    APSDefineMonitoringVariables
    global apsOAGDataDir apsDataLoggerConfigDir
    global apsDataManagerNormalEmail apsDataManagerPagerEmail
    global apsDataManagerNormalEmailOn apsDataManagerPagerEmailOn

    set subDirectory ""
    set rootname ""
    set nextDay 0
    if [APSStrictParseArguments {subDirectory rootname nextDay}]==-1 {
        return -code error "APSStartTimeSeriesLogger: unrecognized arguments"
    }
    if {![string length $subDirectory] || ![string length $rootname]} {
        return -code error "APSStartTimeSeriesLogger: invalid arguments"
    }

    # check for a valid logging directory 
    set dirname $apsOAGDataDir/$subDirectory
    if {![file exists $dirname] || ![file isdirectory $dirname]} {
        return -code error "APSStartTimeSeriesLogger: $dirname not found"
    }
    
    # this call checks for the instruction file, gets the contents for this
    # logger, and sets variables in this routine based on those contents.
    # variables defined are: doRun sampleInterval extraArguments workstation processingScript monitorProgram
    # intervalOptionName doGenerations 
    if [catch {APSReadTimeSeriesLoggerConfig \
                 -subDirectory $subDirectory -rootname $rootname} result] {
        return -code error "APSStartTimeSeriesLogger: $result"
    }
    if [string compare $monitorProgram sddslogger]==0 {
        # these jobs are started by another method
        return
    }
    set sddsglitchlogger 0
    set sddspvaglitchlogger 0
    set input $rootname.mon
    if [string compare $monitorProgram sddsglitchlogger]==0 {
        set sddsglitchlogger 1
        set input $rootname.atrig
    }
    if [string compare $monitorProgram sddspvaglitchlogger]==0 {
        set sddspvaglitchlogger 1
        set input $rootname.atrig
    }
    set sddslogonchange 0
    if [string compare $monitorProgram sddslogonchange]==0 {
        set sddslogonchange 1
	set input ${rootname}.loc
    }
    # check for input file 
    if ![file exists $dirname/$input] {
        return -code error "APSStartTimeSeriesLogger: $dirname/$input not found"
    }
    global apsScriptHost
    set hostname [lindex [split $apsScriptHost .] 0]
    if [string compare $hostname $workstation] {
        return -code error "APSStartTimeSeriesLogger: hostname ($hostname) doesn't match expected $workstation for $subDirectory/$rootname"
    }
    if !$doRun {
        return
    }

    # move to logging directory
    set oldDir [pwd]
    cd $dirname

    # figure out if this is likely to be the first attempt for this 
    # logger today (not crucial).
    # compute the time to run in minutes.
    set timeList [split [exec date +%H:%M] :]
    scan [lindex $timeList 0] %ld hour
    scan [lindex $timeList 1] %ld mins
    set firstTry 0
    if !$nextDay {
        if {$hour==0 && $mins<5} {
            set firstTry 1
        }
        set time [expr int(24*60+1-($hour*60+$mins))]
    } else {
        set firstTry 1
        set time [expr int(24*60+1)]
    }


    # add notation to log file that this is happening.
    if [catch {APSAddToMonitoringLogFile -fileName $rootname.logfile -lineLimit 5000 -text "Checking/starting  (nextday=$nextDay)"} result] {
        cd $oldDir
        return -code error "APSStartTimeSeriesLogger: $result"
    }

    # make the date tag for the output file
    # count the number of files made for today already
    set offset 0
    if $nextDay {
        set offset 1
    } 
    set tag [APSOffsetDateInfo -offset $offset -today 1 -dateFormat Y-J-MD]
    set outputRoot "$rootname-${tag}"
    #set count [llength [glob -nocomplain ${outputRoot}.????]]

    set DataLoggersOnOffOption "-inhibitPV=name=DataLoggersOnOffBO,pend=120,wait=5"
    set DataLoggersOnOffOption ""
    if [string compare $rootname FloorCoord]==0 {
#        global env
#        set env(EPICS_CA_ADDR_LIST) "164.54.85.30"
#        set env(EPICS_CA_AUTO_ADDR_LIST) "NO"
        set DataLoggersOnOffOption ""
    }
    if [string compare $monitorProgram sddswmonitor]==0 {
        set DataLoggersOnOffOption ""
    }

    set rcArguments ""
    if {([llength $RunControlPV]) && ([string range $RunControlPV 0 4] != "Dummy")} {
        global ${RunControlPV}.RUN ${RunControlPV}.CLR
        if {[pv linkw ${RunControlPV}.RUN ${RunControlPV}.RUN] == 0} {
            if {[set ${RunControlPV}.RUN] == 1} {
                return
            }
            if {[pv linkw ${RunControlPV}.CLR ${RunControlPV}.CLR] == 0} {
                set ${RunControlPV}.CLR 1
                pv putw ${RunControlPV}.CLR
            }
        }
        set rcArguments "-runControlPV=string=$RunControlPV,pingTimeout=80 \"-runControlDescription=string=$rootname $monitorProgram\""
    }
    # launch the monitor job
    if $doGenerations {
        if [expr $time>(26.0*60.0)] {
            # don't let any job run more than 26 hours
            # just a failsafe---I don't think this will ever happen.
            catch {APSAddToMonitoringLogFile -fileName $rootname.logfile \
                     -lineLimit 0 -text "time computation problem: time = $time min"}
            set time [expr 26.0*60.0]
        }
        set argList1 [list -generation=digit=4,delimiter=. -time=$time,min]
    } else {
	if !$sddslogonchange {
	    set time [expr 365*24*60.0]
	    set argList1 [list -time=$time,min]
	} else {
	    set argList1 ""
	}
        set outputRoot ""
        if {!$sddsglitchlogger && !$sddspvaglitchlogger} {
            set outputRoot $rootname
            lappend argList1 -dailyFiles
        }
	if {[llength $intervalOptionName]} {
	    eval exec echo $monitorProgram $input $outputRoot $argList1 $rcArguments \
		-$intervalOptionName=$sampleInterval,sec $extraArguments >>& $rootname.logfile
	} else {
	    eval exec echo $monitorProgram $input $outputRoot $argList1 $rcArguments \
		$extraArguments >>& $rootname.logfile
	}
    }
    if {[llength $intervalOptionName]} {
	eval exec echo $monitorProgram $input $outputRoot $argList1 $rcArguments \
	    $DataLoggersOnOffOption \
	    -$intervalOptionName=$sampleInterval,sec $extraArguments >>& $rootname.logfile
	eval exec $monitorProgram $input $outputRoot $argList1 $rcArguments \
	    $DataLoggersOnOffOption \
	    -$intervalOptionName=$sampleInterval,sec $extraArguments >>& $rootname.logfile &
    } else {
	eval exec echo $monitorProgram $input $outputRoot $argList1 $rcArguments \
	    $DataLoggersOnOffOption \
	    $extraArguments >>& $rootname.logfile
	eval exec $monitorProgram $input $outputRoot $argList1 $rcArguments \
	    $DataLoggersOnOffOption \
	    $extraArguments >>& $rootname.logfile &
    }
    # wait for the program to start
    if 0 {
        exec sleep 5
        
        # count the number of files now.  Should be different.
        set outputRoot "$rootname-${tag}"
        set count1 [llength [glob -nocomplain ${outputRoot}.????]]
        if {$count1!=$count} {
            # send notice of start/restart
            if $count {
                set message "Restart $rootname $hostname $hour:[format %02ld $mins]"
            } else {
                set message "Start $rootname $hostname $hour:[format %02ld $mins]"
            }
            if [catch {APSAddToMonitoringLogFile -fileName $rootname.logfile -lineLimit 0 -text "$message"} result] {
                cd $oldDir
                return -code error "$result"
            }
            if $apsDataManagerNormalEmailOn {
                scan [clock format [clock seconds] -format %H:%M] %ld:%ld hour minute
                set hourOfDay [expr $hour+$minute/60.]
                if {$hourOfDay<[expr 23+54/60.0] && $hourOfDay>6/60.} {
                    APSSendEMail -address $apsDataManagerNormalEmail -message "$message"
                }
            } 
            if {!$firstTry && $apsDataManagerPagerEmailOn} {
                APSSendEMail -address $apsDataManagerPagerEmail -message "$message" -considerate 1
            }
        }
    } else {
        set message "Start $rootname $hostname $hour:[format %02ld $mins]"
        if [catch {APSAddToMonitoringLogFile -fileName $rootname.logfile -lineLimit 0 -text "$message"} result] {
            cd $oldDir
            return -code error "$result"
        }
        if 0 {
            if $apsDataManagerNormalEmailOn {
                scan [clock format [clock seconds] -format %H:%M] %ld:%ld hour minute
                set hourOfDay [expr $hour+$minute/60.]
                if {$hourOfDay<[expr 23+54/60.0] && $hourOfDay>6/60.} {
                    APSSendEMail -address $apsDataManagerNormalEmail -message "$message"
                }
            } 
            if {!$firstTry && $apsDataManagerPagerEmailOn} {
                APSSendEMail -address $apsDataManagerPagerEmail -message "$message" -considerate 1
            }
        }
    }
    cd $oldDir
}

# Reads configuration file for a set of time-series data loggers,
# extracting data for a specific logger.
# Sets variables in the calling routine to contain the relevant
# data from the file.
# variables defined are: doRun sampleInterval extraArguments workstation processingScript monitorProgram keepGenerations daysToSave
proc APSReadTimeSeriesLoggerConfig {args} {
    APSDefineMonitoringVariables
    global apsOAGDataDir apsDataLoggerConfigDir

    set subDirectory ""
    set rootname ""
    if [APSStrictParseArguments {subDirectory rootname}]==-1 {
        return -code error "APSReadTimeSeriesLoggerConfig: unrecognized arguments"
    }
    if {![string length $subDirectory] || ![string length $rootname]} {
        return -code error "APSReadTimeSeriesLoggerConfig: invalid arguments"
    }

    # check for instruction file
    set instructionFile $apsDataLoggerConfigDir/timeSeries.config
    if ![file exists $instructionFile] {
        return -code error "APSReadTimeSeriesLoggerConfig: $instructionFile not found"
    }
    # get data for this logger from the instruction file.
    set tmpFile /tmp/[APSTmpString]
    if [catch {exec sddsprocess $instructionFile $tmpFile -nowarning \
                 -match=column,subDirectory=$subDirectory \
                 -match=column,rootname=$rootname \
                 -define=parameter,matchingRows,n_rows,type=long \
                 -process=*,first,%s \
             } result] {
        catch {exec rm $tmpFile}
        return -code error "APSReadTimeSeriesLoggerConfig: $result (sddsprocess)"
    }
    if [catch {sdds open $tmpFile} fid] {
        catch {exec rm $tmpFile}
        return -code error "APSReadTimeSeriesLoggerConfig: $fid (sdds open)"
    }
    if [catch {sdds getParameter $fid matchingRows} matchingRows] {
        catch {sdds close $fid}
        catch {exec rm $tmpFile}
        return -code error "APSReadTimeSeriesLoggerConfig: $matchingRows (sdds getParameter)"
    }
    if $matchingRows!=1 {
        catch {sdds close $fid}
        catch {exec rm $tmpFile}
        return -code error "APSReadTimeSeriesLoggerConfig: $matchingRows matches for $subDirectory/$rootname"
    }
    foreach variable {doRun sampleInterval extraArguments workstation processingScript \
                        intervalOptionName monitorProgram keepGenerations daysToSave \
                        globalProcessingScript doGenerations postprocessingWorkstation \
                        RunControlPV} {
        upvar $variable ${variable}0
        if [catch {sdds getParameter $fid $variable} ${variable}] {
            catch {sdds close $fid}
            catch {exec rm $tmpFile}
            return -code error "APSReadTimeSeriesLoggerConfig: problem reading instructions data for $subDirectory/$rootname: $variable = [subst \$${variable}0]"
        }
        set ${variable}0 [lindex [subst \$$variable] 0]
    }
    catch {sdds close $fid}
    catch {exec rm $tmpFile}
}

# Limit the size of a text file by clipping all but the
# last N lines.
proc APSLimitFileSize {args} {
    set fileName ""
    set lineLimit 500
    if {[APSStrictParseArguments {fileName lineLimit}]==-1 || \
          [string length $fileName]==0 || $lineLimit<1} {
        return -code error "APSLimitFileSize: bad arguments"
    }
    if ![file exist $fileName] {
        return -code error "APSLimitFileSize: $fileName not found"
    }
    set tmpFile $fileName.tmp.[pid]
    if {[file exists $tmpFile] && \
          [catch {exec rm $tmpFile} result]} {
        return -code error "APSLimitFileSize: $result"
    }
    if [catch {exec tail -$lineLimit $fileName > $tmpFile} result] {
        return -code error "APSLimitFileSize: $result"
    }
    if [catch {exec mv $tmpFile $fileName} result] {
        return -code error "APSLimitFileSize: $result"
    }
}

# Add text to a log file, clipping the file at a predefined number of
# lines.
proc APSAddToMonitoringLogFile {args} {
    set fileName ""
    set lineLimit 500
    set text ""
    if {[APSStrictParseArguments {fileName lineLimit text}]==-1 || \
          [string length $fileName]==0} {
        return -code error "APSAddToMonitoringLogFile:  bad arguments"
    }
    if {$lineLimit>0 && [file exists $fileName] && \
          [catch {APSLimitFileSize -fileName $fileName -lineLimit $lineLimit} result]} {
        return -code error "APSAddToMonitoringLogFile: $result"
    }
    if [file exists $fileName] {
        exec echo "[clock format [clock seconds]]: $text" >> $fileName
    } else {
        exec echo "[clock format [clock seconds]]: $text" > $fileName
    }
}


