#!/bin/sh
# startBackgroundEpicsIOC.sh
#
# starts EPICS IOC in background using procServ tool
#
# created: 28-05-2009
# author: p.zumbruch, GSI
# modified: 24-02-2010

#debug - uncomment to debug
#set -xv

#specific predefines
specificPredefines()
{
	myBaseHome=/home/hadaq
	
	myDefaultWorkingDir=${myBaseHome}/EPICS/epics_apps/HadControlTemperatureAndThreshold
	myWorkingDir=${userWorkingDir:-${myDefaultWorkingDir}}
	
	EpicsDefaultIocBootDir=${myWorkingDir}/iocBoot/iocHadControlTemperatureAndThreshold_linux-cris_v10
	EpicsIocBootDir=${userWorkingDir:-${EpicsDefaultIocBootDir}}
	
	EpicsDefaultCommandName="st_linux-cris_v10.cmd"
	EpicsDefaultCommandArguments=""
	
	EpicsCommandName=${userCommandExecutable:-${EpicsDefaultCommandName}}
	EpicsCommandArguments="${userCommandArgument:-${EpicsDefaultCommandArguments}}"
	
	ProcServDefaultDir=${myBaseHome}/EPICS/startupProcedures/procServ
	ProcServDefaultLibDir=${ProcServDefaultDir}/lib
	ProcServDefaultExecutable=${ProcServDefaultDir}/procServ
	
	ProcServDefaultTelnetPort=4813
	ProcServTelnetPort=${userProcServTelnetPort:-${ProcServDefaultTelnetPort}}
	ProcServTelnetLogPort=${userProcServTelnetLogPort}
	ProcServProcName=${userIOC} 
	ProcServLoggingDir=${userProcServLoggingDir}
	ProcServLibDir=${userLibraryPathAdd:-${ProcServDefaultLibDir}}
	ProcServExecutable=${userProcServExecutable:-${ProcServDefaultExecutable}}
}

#predefines
predefines()
{
	scriptname=startBackgroundEpicsIOC.sh

	myVerbosity=0

	myLevel=0
	myExitIfHelperScriptName=exitIfHelpers.sh
	myExitIfHelperScript=$(which ${myExitIfHelperScriptName})
	myExitIfHelperScript=${myExitIfHelperScript:-$(dirname $0)/${myExitIfHelperScriptName}}
}

#unset function
unsets()
{
	unset myHostname mylist item
	unset myWorkingDir
	unset EpicsIocBootDir
	unset EpicsCommandName
	unset EpicsCommandArguments
	unset EpicsDefaultCommandName
	unset EpicsDefaultCommandArguments
	
	unset myDefaultWorkingDir
	unset EpicsDefaultIocBootDir
	
	unset ProcServDefaultDir
	unset ProcServDefaultLibDir
	unset ProcServDefaultExecutable
	unset ProcServLibDir
	unset ProcServExecutable
	unset ProcServDefaultTelnetPort
	unset ProcServTelnetPort
	unset ProcServTelnetLogPort
	unset ProcServProcName
	unset ProcServFileLogging
	unset ProcServLoggingDir
	
	unset myDebug
	unset myHelp
	unset myLevel
	unset myExitIfHelperScript
	unset myExitIfHelperScriptName
	unset myEcho
	unset ProcServDebug
	unset ProcServOptions
	
	unset myWorkingDirOpt;		unset myWorkingDirOptArg
	unset EpicsIocBootDirOpt;	unset EpicsIocBootDirOptArg
	unset EpicsCommandNameOpt;	unset EpicsCommandNameOptArg
	unset EpicsCommandArgumentsOpt;	unset EpicsCommandArgumentsOptArg
	unset ProcServFileLoggingOpt;	unset ProcServFileLoggingOptArg
	unset ProcServLoggingDirOpt;	unset ProcServLoggingDirOptArg
	unset ProcServTelnetPortOpt;	unset ProcServTelnetPortOptArg
	unset ProcServTelnetLogPortOpt;	unset ProcServTelnetLogPortOptArg
	unset ProcServExecutableOpt;	unset ProcServExecutableOptArg
	unset ProcServLibDirOpt;	unset ProcServLibDirOptArg
	unset ProcServProcNameOpt;	unset ProcServProcNameOptArg
	unset myBaseHomeOpt;		unset myBaseHomeOptArg
	unset myExitIfHelperScriptOpt;	unset myExitIfHelperScriptOptArg
	unset myHelpOpt;		unset myHelpOptArg
	unset myEchoOpt;		unset myEchoOptArg
	unset myDebugOpt;		unset myDebugOptArg
	unset ProcServDebugOpt;		unset ProcServDebugOptArg
	unset myVerbosityOpt;		unset myVerbosityOptArg
	unset myQuietOpt;		unset myQuietOptArg
	unset ProcServOptionsOpt;	unset ProcServOptionsOptArg
	
	return 0;
}

#usage function
usage()
{
	echo
	echo "$scriptname"
	echo    "usage: $scriptname [options]"
	echo
	echo    "starts EPICS IOC in background using procServ tool,"
	echo    "  which spawns and automatically respawns the ioc process "
	echo    "  and makes it available via local telnet:"
	echo    "   telnet localhost port"
	echo    "        where port is set to default \`${ProcServTelnetPort}'"
	echo    "options:"
	echo    "   -${myWorkingDirOpt}"           "workingDir   : EPICS IOC application dir / a.k.a. workingDir (absolute path)"
	echo    "                      [\`${myWorkingDir}']"
	echo    "   -${EpicsIocBootDirOpt}"        "iocBootdir   : EPICS IOC iocboot dir "
	echo    "                      place where to change to and where log and pid files may be written (absolute path)"
	echo    "                      [\`${EpicsIocBootDir}']"
	echo    "   -${EpicsCommandNameOpt}"       "command file : st.cmd file started relativ to \`./' "
	echo    "                      if not absolute path, taken relatively to \`EpicsIocBootDir'"
	echo    "                      [\`${EpicsCommandName}']"
	echo    "   -${EpicsCommandArgumentsOpt}"  "commandArgs  : [quoted list] of argument[s] for executable of ioc process,"
	echo    "                      [\`\"$EpicsCommandArguments\"']"
	echo    "   -${ProcServExecutableOpt}"     "procServ exe : procServ executable"
	echo    "                      [\`${ProcServExecutable}']"
	echo    "   -${ProcServLibDirOpt}"         "procServ lib : procServ lib path needed to be added to LD_LIBRARY_PATH, multiple calls possible"
	echo    "                      [\`${ProcServLibDir}']"
	echo    "   -${ProcServTelnetPortOpt}"     "port         : telnet port [\`${ProcServTelnetPort}'], see above"
	echo    "   -${ProcServTelnetLogPortOpt}"  "logport      : set log readonly telnet port for active procServ access (NOTE: default log port will be port+1)"
	echo    "                      [\`$ProcServTelnetLogPort']"
	echo	"   -${ProcServFileLoggingOpt}"    "             : activate file logging for procServ [$([ -z "${myProcServFileLogging}" ] && echo OFF || echo ON)]"
	echo    "   -${ProcServLoggingDirOpt}"     "loggingDir   : file logging directory,"
	echo    "                      if not absolute path, taken relatively to \`workingDir' [\`$myWorkingDir']"
	echo    "                      [\`$ProcServLoggingDir']"
	echo -n "   -${ProcServDebugOpt}"          "             : debug mode of procServ"
	echo 	" [$([ -z "${ProcServDebug}" ] && echo OFF || echo ON)]"
	echo    "   -${ProcServProcNameOpt}"       "name         : name of (IOC) process"
	echo    "                      [\`${ProcServProcName}']"
	echo    "   -${ProcServOptionsOpt}"      "\"options\"    : additional options for procServ"
	echo    "                      [\`${ProcServOptions}']"
	echo    "   General: "
	echo 	"   -${myBaseHomeOpt}"             "baseHome     : home directory of hadaq"
	echo    "                      [\`$myBaseHome']"
	echo -n "   -${myEchoOpt}"                 "             : no action, just echos command instead of executing the ioc process"
	echo    " [$([ -z "${myEcho}" ] && echo OFF || echo ON)]"
	echo -n "   -${myDebugOpt}"                "             : debug mode of caller, sets set -xv"
	echo 	" [$([ -z "${myDebug}" ] && echo OFF || echo ON)]"
	echo -n "   -${myVerbosityOpt}"            "             : verbosity of caller, can be called repeatedly "
	echo 	" [$([ 0 -eq ${myVerbosity} ] && echo OFF || echo ON)$([ 1 -lt ${myVerbosity} ] && echo \($myVerbosity\))]"
	echo    "   -"${myExitIfHelperScriptOpt}   "scriptname   : helper script internally used by \`$scriptname' for checks"
	echo    " 			    defaults to use primarily \`which' to find it and secondly to "
	echo    "               be searched in the same directory as \'$scriptname'"
	echo    "                      [\`${myExitIfHelperScript}']"
	echo -n "   -"${myQuietOpt}                "             : sets verbosity of caller off"
	echo    " [$([ -z "${myQuiet}" ] && echo OFF || echo ON)]"
	echo    "   -${myHelpOpt}"                 "             : this help"
	echo

	echo
}

#eval options function
evalOptions()
{
	myWorkingDirOpt="w";	      myWorkingDirOptArg=':'
	EpicsIocBootDirOpt="i";	      EpicsIocBootDirOptArg=':'
	EpicsCommandNameOpt="c";	  EpicsCommandNameOptArg=':'
	EpicsCommandArgumentsOpt="a"; EpicsCommandArgumentsOptArg=':'
	ProcServFileLoggingOpt="g";
	ProcServLoggingDirOpt="G";	  ProcServLoggingDirOptArg=':'
	ProcServTelnetPortOpt="p";	  ProcServTelnetPortOptArg=':'
	ProcServTelnetLogPortOpt="t"; ProcServTelnetLogPortOptArg=':'
	ProcServExecutableOpt="P";	  ProcServExecutableOptArg=':'
	ProcServLibDirOpt="L";	      ProcServLibDirOptArg=':'
	ProcServProcNameOpt="I";	  ProcServProcNameOptArg=':'
	ProcServOptionsOpt="o";	      ProcServOptionsOptArg=':'
	myBaseHomeOpt="b";	          myBaseHomeOptArg=':'
	myExitIfHelperScriptOpt="e";  myExitIfHelperScriptOptArg=':'
	myHelpOpt="h";
	myEchoOpt="n";
	myDebugOpt="d";
	ProcServDebugOpt="D";
	myVerbosityOpt="v";
	myQuietOpt="q";

	#evalOptions $@
	#evaluates the options of the command line
	local opts="myWorkingDir EpicsIocBootDir EpicsCommandName EpicsCommandArguments ProcServFileLogging ProcServLoggingDir ProcServTelnetPort  ProcServTelnetLogPort ProcServExecutable ProcServLibDir ProcServProcName myBaseHome myExitIfHelperScript myHelp myEcho myDebug myVerbosity myQuiet ProcServDebug ProcServOptions"

	for item in $(echo $opts | sed -e "s/ /$IFS/g")
	do
		options="${options}\${${item}Opt}\${${item}OptArg}"
	done
	options=$(eval echo $options)

	while getopts $options arg
	do
		case $arg in
		${myWorkingDirOpt} )			myWorkingDir=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${EpicsIocBootDirOpt} )			EpicsIocBootDir=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${EpicsCommandNameOpt} )		EpicsCommandName=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${EpicsCommandArgumentsOpt} )	EpicsCommandArguments=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServFileLoggingOpt} )		ProcServFileLogging=yes
			shift;;
		${ProcServLoggingDirOpt} )		ProcServLoggingDir=$(echo $OPTARG | sed -e 's/"//g')
										ProcServFileLogging=yes
			shift 2;;
		${ProcServTelnetPortOpt} )		ProcServTelnetPort=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServTelnetLogPortOpt} )	ProcServTelnetLogPort=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServExecutableOpt} )		ProcServExecutable=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServLibDirOpt} )			[ -d $(echo $OPTARG | sed -e 's/"//g') ] &&
											ProcServLibDir=${ProcServLibDir:+${ProcServLibDir}\:}$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServProcNameOpt} ) 		ProcServProcName=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${ProcServDebugOpt} )			ProcServDebug=yes
			shift;;
		${ProcServOptionsOpt} )			ProcServOptions=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${myBaseHomeOpt})				myBaseHome=$(echo $OPTARG | sed -e 's/"//g')
			shift 2;;
		${myExitIfHelperScriptOpt} )	myExitIfHelperScript=$(echo $OPTARG | sed -e 's/"//g')
										[ ! -z "${myExitIfHelperScript##/*}" ] &&
											myExitIfHelperScript="$(cd $(dirname ${0}) 2>/dev/null && pwd)/${myExitIfHelperScript}"
			shift 2;;
		${myHelpOpt} )					myHelp=yes
			shift;;
		${myEchoOpt} )					myEcho="echo -e"
			shift;;
		${myDebugOpt} )					myDebug=yes
			shift;;
		${myVerbosityOpt} )				myVerbosity=$(($myVerbosity + 1))
			shift;;
		${myQuietOpt} )					myQuiet=yes
										myVerbosity=0
			shift;;
		esac
	done
}

#dump function
dump()
{
	[ 3 -le $myVerbosity ] && myLevel=$(($myLevel+1)) && echo [${myLevel}] "entering function \"${FUNCNAME:-dump} $*\""
	echo -e " settings are:\n"
	local mv=$myVerbosity;
	local mvc=$myVerbosityCallee;
	echo -e "                \"scriptname \": \t" ${scriptname}
	echo -e "      \"myExitIfHelperScript \": \t" ${myExitIfHelperScript}
	echo -e "              \"myWorkingDir \": \t" ${myWorkingDir}
	echo -e "           \"EpicsIocBootDir \": \t" ${EpicsIocBootDir}
	echo -e "          \"EpicsCommandName \": \t" ${EpicsCommandName}
	echo -e "     \"EpicsCommandArguments \": \t" ${EpicsCommandArguments}
	echo -e "       \"ProcServFileLogging \": \t" ${ProcServFileLogging}
	echo -e "        \"ProcServLoggingDir \": \t" ${ProcServLoggingDir}
	echo -e "        \"ProcServTelnetPort \": \t" ${ProcServTelnetPort}
	echo -e "     \"ProcServTelnetLogPort \": \t" ${ProcServTelnetLogPort}
	echo -e "        \"ProcServExecutable \": \t" ${ProcServExecutable}
	echo -e "            \"ProcServLibDir \": \t" ${ProcServLibDir}
	echo -e "           \"ProcServOptions \": \t" ${ProcServOptions}
	echo -e "          \"ProcServProcName \": \t" ${ProcServProcName}
	echo -e "             \"ProcServDebug \": \t" "[$([ -z "${ProcServDebug}" ]	&& echo OFF || echo ON)]"
	echo -e "                \"myBaseHome \": \t" ${myBaseHome}
	echo -e "      \"myExitIfHelperScript \": \t" ${myExitIfHelperScript}
	echo -e "                    \"myHelp \": \t" "[$([ -z "${myHelp}" ]	&& echo OFF || echo ON)]"
	echo -e "                    \"myEcho \": \t" "[$([ -z "${myEcho}" ]	&& echo OFF || echo ON)]"
	echo -e "                   \"myDebug \": \t" "[$([ -z "${myDebug}" ]	&& echo OFF || echo ON)]"
	echo -e "               \"myVerbosity \": \t" "[$([ 0 -eq ${mv} ]	&& echo OFF || echo ON)$([ 1 -lt ${mv} ] && echo \($mv\))]"
	echo -e "                   \"myQuiet \": \t" "[$([ -z "${myQuiet}" ]	&& echo OFF || echo ON)]"
	echo -e

	[ 3 -le $myVerbosity ] && echo [${myLevel}] " leaving function\"${FUNCNAME:-dump}\"" && myLevel=$(($myLevel-1))
}

#function help
help()
{
	usage ;
	unsets;
	exit 0;
}

#checks function
checks()
{
	# checks and twiddling run
	# performs checks on all settings
	# and complete path and pathnames

	[ 3 -le $myVerbosity ] && myLevel=$(($myLevel+1)) && echo [${myLevel}] "entering function \"${FUNCNAME:-checks} $*\""

	# Checks for mandatory absolute paths, if not exit
	[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"... checks for mandatory absolute paths: base home - $myBaseHome\""
	if [ ! -z ${myBaseHome}   ]
	then
		exitIfNotAbsolutePath ${myBaseHome} "base home" &&
		exitIfNotADirectory ${myBaseHome} "base home" &&
		myBaseHome="$(cd ${myBaseHome} 2>/dev/null && pwd)"
	fi

	[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"... checks for mandatory absolute paths: workingDir - $myWorkingDir\""
	if [ ! -z ${myWorkingDir}   ]
	then
		exitIfNotAbsolutePath ${myWorkingDir} "working Directory"
		exitIfNotADirectory ${myWorkingDir} "working Directory"
		myWorkingDir="$(cd ${myWorkingDir} 2>/dev/null && pwd)"
	else
		myWorkingDir=$(pwd)
	fi

	# Convert the rest to absolute paths
	[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"... convert the rest to absolute paths and check them:\""

	# if not absolute path, taken relatively to `workingDir'
	# IOC Boot Dir
	if [ ! -z ${EpicsIocBootDir} ]
	then
		[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"   ... iocBootDir:\""
		isAbsolutePath ${EpicsIocBootDir} || EpicsIocBootDir=${myWorkingDir}/${EpicsIocBootDir}
		exitIfNotADirectory ${EpicsIocBootDir}  "EPICS IOC Boot directory"
		EpicsIocBootDir="$(cd ${EpicsIocBootDir} 2>/dev/null && pwd)"
	fi

	# if not absolute path, taken relatively to `workingDir'
	# ProcServ Logging Dir
	if [ ! -z ${ProcServLoggingDir} ]
	then
		[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"   ... procServ Logging Dir:\""
		isAbsolutePath ${ProcServLoggingDir}	|| ProcServLoggingDir=${myWorkingDir}/${ProcServLoggingDir}
		exitIfNotADirectory ${ProcServLoggingDir}  "procServ Logging directory"
		ProcServLoggingDir="$(cd ${ProcServLoggingDir} 2>/dev/null && pwd)"
	fi

	# if not absolute path, taken relatively to `EpicsIocBootDir'
	# command Executable

	if [ ! -z ${EpicsCommandName} ]
	then
		[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"   ... EpicsCommandName:\""
		isAbsolutePath ${EpicsCommandName} ||	EpicsCommandName=${EpicsIocBootDir:+./}${EpicsCommandName}
		exitIfNotExecutable ${EpicsIocBootDir:+${EpicsIocBootDir}/}${EpicsCommandName} "command executable"
	fi

	#procServ Library Paths
	# 	[ ! -z ${ProcServLibDir} ] &&
	# 	for item in $(echo ${ProcServLibDir} | sed -e 's/:/ /g')
	# 	do
	# 		exitIfNotADirectory ${item} "procServ Lib dir \`${ProcServLibDir}' contains invalid directory"
	# 	done

	#procServ executable:
	# if not absolute path, taken relatively to `baseHome'

	if [ ! -z ${ProcServExecutable} ]
	then
		[ 3 -le $myVerbosity ] && echo [${myLevel}] "\"   ... ProcServExecutable:\""
		isAbsolutePath ${ProcServExecutable} || ProcServExecutable=${myBaseHome}/${ProcServExecutable}
		exitIfNotExecutable "${ProcServExecutable}" "procServ executable"
		ProcServExecutable="$(cd $(dirname ${ProcServExecutable}) 2>/dev/null && pwd)/$(basename ${ProcServExecutable})"
	fi

	[ 3 -le $myVerbosity ] && echo [${myLevel}] " leaving function\"${FUNCNAME:-checks}\"" && myLevel=$(($myLevel-1))
}

#execute function
execute()
{

	local function="${FUNCNAME:-execute}"
	[ 3 -le $myVerbosity ] && myLevel=$(($myLevel+1)) && echo [${myLevel}] "entering function \"$function $*\""

	local options;
	options=" --ignore ^D^C --coresize 0 "
	options=${options}${ProcServProcName:+ --name "${ProcServProcName}" }
	options=${options}${ProcServProcName:+ --pidfile ${myWorkingDir}/${ProcServProcName}.pid }
	options=${options}${EpicsIocBootDir:+ --chdir ${EpicsIocBootDir} }
	#	[ ! -z "${ProcServFileLogging}" ] &&
		options=${options}${ProcServLoggingDir:+ --logfile ${ProcServLoggingDir}/${ProcServProcName}.log }
	options=${options}${ProcServTelnetLogPort:+ --logport ${ProcServTelnetLogPort} }
	options=${options}${ProcServDebug:+ --debug }
	options=${options}${ProcServOptions:+ ${ProcServOptions} }

	local arguments;
	arguments="${arguments}${ProcServTelnetPort} "
	arguments="${arguments}${EpicsCommandName}${EpicsCommandArguments:+ ${EpicsCommandArguments}}"

	[ 1 -le $myVerbosity ] && echo -e  "     calling procServ [options] ${arguments}\n"
	[ 2 -le $myVerbosity ] && echo -e  "     options  : \n$options \n"
	[ 2 -le $myVerbosity ] && echo -e  "     arguments: \n$arguments \n"

	local ldLibPath=${ProcServLibDir:+LD_LIBRARY_PATH=${ProcServLibDir}${LD_LIBRARY_PATH+:}${LD_LIBRARY_PATH}}
	local showAll="export ${ldLibPath} \&\& ${ProcServExecutable} $showOptions $showArguments"

	[ 2 -le $myVerbosity ] && echo -e "complete command:\n ${myEcho} $all \n"
	# here the command execution takes place
	${myEcho:+$myEcho -n } export ${ldLibPath} && ${myEcho:+$myEcho " "\&\&\\\n } ${ProcServExecutable} $options $arguments

	[ 3 -le $myVerbosity ] && echo [${myLevel}] " leaving function\"$function\"" && myLevel=$(($myLevel-1))
}

###########################################
### main
    unsets
    ${useUserDefinedVariables:+userDefines}
    specificPredefines
    predefines
#eval options
		oldIFS=$IFS #store old IFS
		IFS="|"     #change IFS to handle quotes when passing args to functions
	evalOptions $(echo "$*""$IFS" | sed -e "s/\(-[a-zA-Z0-9]$IFS\)\([^-]*\)\($IFS\)/\1\"\2\"\3/g")
		IFS=$oldIFS #reset IFS

#if log port not set, then logport = port + 1
	[ -z "$ProcServTelnetLogPort" ] && ProcServTelnetLogPort=$((${ProcServTelnetPort} + 1))

#if name hasn't been set, then set the default
	[ -z "$ProcServProcName" ] && ProcServProcName="$(hostname):${ProcServTelnetPort}_${EpicsCommandName}"

#call Help if help option is set
	[ ! -z "$myHelp" ] && help;

#load helper functions
	if [ -f "$myExitIfHelperScript" ]
	then
		. ${myExitIfHelperScript}
	else
		echo "$scriptname: helper script \`$myExitIfHelperScript' does not exists ... exiting" 1>&2
		unsets
		exit 1;
	fi

	[ 1 -le $myVerbosity ] && echo $scriptname ...
	[ 2 -le $myVerbosity ] && dump;

# debug settings
	[ ! -z "$myDebug" ] && set -x -v

#check directories and permissions
	checks
#execute function
	execute
#unsets
	unsets

exit 0
