#!/bin/bash
#!-*- sh -*-
#
# Copyright: See the COPYRIGHT that is included with this distribution.
#            EPICS exampleCPP is distributed subject to a Software License
#            Agreement found in file LICENSE that is included with this
#            distribution.
#
# Abs: gethist queries the Channel Archive Server for the data for the channel 
#      specified between two dates and times and outputs the data according to
#      supplied formatting options
#
# Rem: gethist creates an Archiver Client which is an example of an EPICS v4 
#      service client, which gets archived data from the Archiver Service,
#      an example of a v4 RPC service.
# 
#
# Usage: see help (gethist -h or gethist --help) for options                   
# 
# ----------------------------------------------------------------------------
# Auth: Dave Hickin (david.hickin@diamond.ac.uk)
# ============================================================================


TOP=$(dirname $0)

# change this to query an archive service of a different name by default
DEFAULT_SERVICE="archiveService"

Error()
{
    echo "Error: $@" >&2;
    usage
    exit 1;
}


usage()
{
cat << EOF
Usage
    gethist [OPTIONS] channel...
    gethist [-h|--help]
    see gethist --help for details

EOF
}


showhelp()
{
cat << EOF
Name
    gethist

EOF

usage

cat << EOF

Options
    Mandatory arguments to long options are mandatory for short options too.

    -h,--help              display help
    -S,--service=SERVICE   query the archiver service with name SERVICE.
                           Default service name is $DEFAULT_SERVICE.
    -s,--start=START_TIME  query archive for results starting from START_TIME
    -e,--end=END_TIME      query archive for results up to END_TIME
    -M,--max-records=MAX   limit results to first MAX records
    -f,--file=FILENAME     output results of archiver query to file named
                           FILENAME
    -n,--title             print channel name, preceded by #, before results
                           after channel name, if printed.
    -t,--transpose         transpose rows and columns
    -T,--headers           print column headers, preceded by #, before results
                           after channel name, if printed.
    -q,--quiet             suppress all output to standard out except archive
                           data.
    -v,--verbose           output verbose logging information.    
    -x,--scientific        results of archiver query request outputted using
                           scientific (i.e. exponent/mantissa) format 
    -d,--fixed             results of archiver query request outputted using
                           scientific (i.e. exponent/mantissa) format
    -p,--precision=PREC    results of archiver query request given to precision
                           PREC
                           Default value is 6.
    -o, --fields           specifies which fields to output, in which order 

Description
    gethist queries the Channel Archive Server for the data for the channel 
    specified by channel argument between the -s,--start and -e,--end flags.
    The format for the start and end parameters is that of the date -d command.
    The defaults are "1 hour ago" for start and "now" for the end.

    The data returned by the archive server is outputted in columns, formatted 
    according to the -x,--scientific, -d,--fixed and -p,--precision options. 
    For the fixed option, the precision specifies the number of decimal places.
    For scientific it specifies the number of decimal places of the mantissa.
    The default is neither scientific nor fixed formatting. In this case the
    value is formatted as fixed or scientific, the format being value-dependent.

    If the -n,--title option is selected the channel name, preceded by'#', is 
    outputted before the archived values. If the -t,--header option is selected
    the column name, preceded by'#', is outputted before the values.

    The choice of fields and the order in which they are outputted can be
    selected by the -o,--fields option. The argument for this option consist of
    a sequence of characters representing the possible fields
    t    floating point time, in seconds, past EPICS epoch
    v    the archived value
    D    the date and time string of the archived value
    A    A string based on the alarm status and severity 
    s    seconds past EPICS epoch
    n    nanoseconds
    S    The alarm status, as an integer
    V    The alarm severity, as an integer

    Output by default goes to the standard output along with any logging
    information. It can be directed to a file using the -f,--file option. 
    Logging information will still be outputted to standard out.
EOF

exit 0
}


EPICS_EPOCH=631152000

CHANNEL=
SERVICE=$DEFAULT_SERVICE
START_ARG="1 hour ago"
END_ARG="now"
COUNT_ARG=
FILE=
PRECISION=6

OPTIONS=

DISPLAYED_FIELDS="tvDA"



process_shortopts()
{

while getopts :hS:s:e:M:f:ao:p:dxntTqv OPTION
do
    case $OPTION in
    h) showhelp
       ;;

    S) SERVICE="$OPTARG"
       ;;

    s) START_ARG="$OPTARG"
       ;;

    e) END_ARG="$OPTARG"
       ;;

    M) COUNT_ARG="$OPTARG"
       ;;

    f) FILE="$OPTARG"
       ;;

    a) OPTIONS+="a"
       ;;

    o) DISPLAYED_FIELDS="$OPTARG"
       ;;

    p) PRECISION="$OPTARG"
       ;;

    x) OPTIONS+="x"
       ;;

    d) OPTIONS+="d"
       ;;

    n) OPTIONS+="n"
       ;;

    t) OPTIONS+="t"
       ;;

    T) OPTIONS+="T"
       ;;

    q) OPTIONS+="q"
       ;;

    v) OPTIONS+="v"
       ;;

    ?) Error invalid option or missing argument ;;
    esac
done
shift $((OPTIND-1))


#check channel supplied
(($# > 0))  ||  Error "no channel supplied"


CHANNEL="$1"

if [ "$CHANNEL" = "" ]
then
    Error "no channel supplied"
fi


# Convert start time supplied to seconds past EPICS epoch
# and check validity
UNIX_START=$(date -d "$START_ARG" '+%s')

if [ "$?" -ne 0 ]  || ! [[ "$UNIX_START" =~ [0-9+] ]]
then
    echo $UNIX_START
    Error "Invalid UNIX start time"
fi

START=$((UNIX_START-EPICS_EPOCH))


# Convert end time supplied to seconds past EPICS epoch
# and check validity
UNIX_END=$(date -d "$END_ARG" '+%s')

if [ "$?" -ne 0 ]  || ! [[ "$UNIX_END" =~ [0-9+] ]]
then
    echo $UNIX_END
    Error "Invalid UNIX end time"
fi

END=$((UNIX_END-EPICS_EPOCH))


#check compatibility of options and supply default if required
if [[ "$OPTIONS" =~ "d" ]] && [[ "$OPTIONS" =~ "x" ]]
then
    Error "Fixed-point and scientific options incompatible"
fi

run_client "$@"
}


run_client()
{

BIN=$TOP/bin/$EPICS_HOST_ARCH/ArchiverClient

ARGS=('-S' "$SERVICE" '-s' "$START" '-e' "$END")

if [[ "$COUNT_ARG" != "" ]]
then
   ARGS+=('-M' "$COUNT_ARG")
fi

ARGS+=('-p' "$PRECISION")

if [[ "$DISPLAYED_FIELDS" != "" ]]
then
    ARGS+=('-o' "$DISPLAYED_FIELDS")
fi

if [[ "$FILE" != "" ]]
then
    ARGS+=('-f' "$FILE") 
fi

if [ "$OPTIONS" != "" ]
then
   ARGS+=("-$OPTIONS")
fi

$BIN "${ARGS[@]}" "$@"
}


process()
{
    arg=("$@")

    # convert long opts to short opts
    for i in $(seq 0 $((${#arg[@]}-1)))
    do
        ARG=${arg[$i]} 
        if [[ "$ARG" =~ ^-- ]]
        then
            case $ARG in
            --help)
                NEW_ARG="-h";;

            --service )
                NEW_ARG="-S";;

            --start )
                NEW_ARG="-s";;

            --end )
                NEW_ARG="-e";;

            --max-records )
                NEW_ARG="-M";;

            --file )
                NEW_ARG="-a";;

            --append )
                NEW_ARG="-f";;

            --fields )
                NEW_ARG="-o";;
 
            --scientific )
                NEW_ARG="-x";;

            --fixed )
                NEW_ARG="-d";;

            --precision )
                NEW_ARG="-p";;

            --title )
                NEW_ARG="-n";;

            --transpose )
                NEW_ARG="-t";;

            --headers )
                NEW_ARG="-T";;

            --quiet )
                NEW_ARG="-q";;

            --verbose )
                NEW_ARG="-v";;
            *)
                Error "Illegal long option $ARG"
                ;;            
           esac
           arg[$i]="$NEW_ARG"
        else
            NEW_ARG="$ARG"
        fi
    done
    
    process_shortopts "${arg[@]}"
}



process "$@"



