#!/bin/sh
#------------------------------------------------------------------------------
# =========                 |
# \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
#  \\    /   O peration     | Website:  https://openfoam.org
#   \\  /    A nd           | Copyright (C) 2015-2019 OpenFOAM Foundation
#    \\/     M anipulation  |
#------------------------------------------------------------------------------
# License
#     This file is part of OpenFOAM.
#
#     OpenFOAM is free software: you can redistribute it and/or modify it
#     under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 3 of the License, or
#     (at your option) any later version.
#
#     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
#     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
#     for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
#
# Script
#     foamMonitor
#
# Description
#     Monitor data with Gnuplot from time-value(s) graphs written by OpenFOAM
#     e.g. by functionObjects
#     - requires gnuplot
#
#------------------------------------------------------------------------------
usage() {
    cat<<USAGE

Usage: ${0##*/} [OPTION] <file>
options:
  -help     | -h         print the usage
  -idle     | -i <time>  stops if <file> unchanging for <time> sec (default 60s)
  -title    | -t <title> set the graph title (default "Data Monitoring")
  -logscale | -l         plots data (y-axis) on log scale, e.g. for residuals
  -refresh  | -r <time>  refreshes display every <time> sec (default 10s)
  -yrange   | -y <range> sets data (y-axis) range, format "[0:1]"
  -size     | -s <size>  set the size of the output plot, format "640,480"
  -ascii    | -a         show the graph by printing ascii in the terminal

Monitor data with Gnuplot from time-value(s) graphs written by OpenFOAM
e.g. by functionObjects
- requires gnuplot

Example:
  foamMonitor -l postProcessing/residuals/0/residuals.dat

USAGE
}

error() {
    exec 1>&2
    while [ "$#" -ge 1 ]; do echo "$1"; shift; done
    usage
    exit 1
}

plotFileHeader() {
    cat<<EOF
set term $gpterminal $size
$logscale
$yrange
set ytics format "$yticFormat"
set title "$title"
set xlabel "$xlabel"
plot \\
EOF
}

plotFileFooter() {
    cat<<EOF

pause $refresh
reread
EOF
}

howMany() ( echo "$1" | awk '{ print NF }' )
#howMany() ( echo "$1" | set -f; set -- $1; echo $# )

isInteger() {
    [ ! -z "${1##*[!0-9]*}" ] && echo "$1" && return 0
}

idle=60
refresh=10
logscale=""
yrange=""
yticFormat="%g"
title="Data Monitoring"
size=""
gpterminal='x11 1 font "helvetica,17" linewidth 1.5 persist noraise'

command -v gnuplot >/dev/null 2>&1 || error "Gnuplot not installed"

# parse options
while [ "$#" -gt 0 ]
do
    case "$1" in
    -h | -help)
        usage && exit 0
        ;;
    -i | -idle)
        [ "$#" -ge 2 ] || error "'$1' option requires an argument"
        idle=$(isInteger "$2") || error "Argument of '$1' is not an integer: '$2'"
        shift 2
        ;;
    -l | -logscale)
        logscale="set logscale y"
        yticFormat="%1.e"
        shift 1
        ;;
    -t | -title)
        [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
        title=$2
        shift 2
        ;;
    -r | -refresh)
        [ "$#" -ge 2 ] || error "'$1' option requires an argument"
        refresh=$(isInteger "$2") || error "Argument of '$1' is not an integer: '$2'"
        shift 2
        ;;
    -y | -yrange)
        [ "$#" -ge 2 ] || error "'$1' option requires an argument"
        yrange="set yrange $2"
        shift 2
        ;;
    -s | -size)
        [ "$#" -ge 2 ] || error "'$1' option requires an argument"
        size="size $2"
        shift 2
        ;;
    -a | -ascii)
        gpterminal="dumb"
        shift 1
        ;;
    -*)
        error "unknown option: '$*'"
        ;;
    *)
        break
        ;;
    esac
done

[ $# -eq 1 ] || error "Incorrect arguments specified"
[ -f "$1" ]  || error "File $1 does not exit"
file=$1

# Get keys from header
keys=$(grep -E '^#' "$file" | tail -1)

[ "x$keys" = "x" ] && keys="# Step"
n_keys=$(howMany "$keys")
n_cols=$(tail -1 "$file" | awk '{ print NF }')

# With full column labels, n_keys = n_cols + 1, since it includes "#"

# If n_keys > n_cols + 1, REMOVE EXCESS keys
n_cols_pone=$(( n_cols + 1 ))
[ "$n_keys" -gt "$n_cols_pone" ] && \
    keys=$(echo "$keys" | cut -d" " -f1-$n_cols_pone)
n_keys=$(howMany "$keys")

i=0
while [ "$n_keys" -le "$n_cols" ]
do
    i=$(( i + 1 ))
    keys="$keys data$i"
    n_keys=$(howMany "$keys")
done

# Remove # and Time keys
xlabel=$(echo "$keys" | cut -d " " -f2)
keys=$(echo "$keys" | cut -d " " -f3-)

gp_file=$(mktemp /tmp/tmp.XXXXXX)
plotFileHeader > "$gp_file"
i=1
for field in $keys
do
    i=$(( i + 1 ))
    plot_line="\"$file\" using 1:${i} with lines title \"$field\""
    if [ $i -lt "$n_cols" ]
    then
       plot_line="$plot_line, \\"
    fi
    echo "$plot_line" >> "$gp_file"
done
plotFileFooter >> "$gp_file"

touch "$file"
gnuplot "$gp_file" &
pid=$!

while true
do
    mod_time=$(stat --format=%Y "$file")
    idle_ago=$(( $(date +%s) - idle ))
    test "$mod_time" -gt "$idle_ago" || break
    sleep "$refresh"
done

kill -9 $pid
rm "$gp_file"

#------------------------------------------------------------------------------
