]> Freerunner's - dotfiles.git/commitdiff
bin: add sensors-plotter script
authorAndre Ramnitz <tux.rising@gmail.com>
Mon, 12 Aug 2024 16:12:13 +0000 (18:12 +0200)
committerAndre Ramnitz <tux.rising@gmail.com>
Mon, 26 Aug 2024 13:52:01 +0000 (15:52 +0200)
local/bin/sensors-plotter [new file with mode: 0755]

diff --git a/local/bin/sensors-plotter b/local/bin/sensors-plotter
new file mode 100755 (executable)
index 0000000..895e59f
--- /dev/null
@@ -0,0 +1,210 @@
+#!/bin/ksh
+# upstream url: https://github.com/ezonakiusagi/sensors-plotter
+
+##
+## USAGE definition
+##
+# usage: sensors-plotter [options]
+#   -c, --chip=chip        select sensor chip
+#   -s, --sensor=sensor            select specific sensor within selected chip
+#   -w, --width=seconds            the number of seconds of the width of plot
+#   -t, --title=title      set the title of the plot
+#   -S, --stats                    turn on statistics at bottom of screen
+#   -C, --continue=file     continue from previous data stream file
+USAGE=$'[-?\n@(#)$Id: 0.1\n]'
+USAGE+="[-author?Art of Server <the.art.of.server@gmail.com>]"
+USAGE+="[-copyright?Copyright (C) 2019 The Art of Server.]"
+USAGE+="[-license?GNU General Public License v3.]"
+USAGE+="[+NAME?sensors-plotter --- plot graphs of sensors data stream in terminal.]"
+USAGE+="[+DESCRIPTION?sensor-plotter is a small script to plot graphs in a terminal "
+USAGE+="using a stream of data points from the sensors command.]"
+USAGE+="[c:chip]:[sensor chip?Select the sensor chip to probe (required).]"
+USAGE+="[s:sensor]:[sensor?Select the sensor to probe (required).]"
+USAGE+="[w:width]:[width?The number of seconds of the width of plot (default=120).]"
+USAGE+="[t:title]:[title?The title of the plot.]"
+USAGE+="[S:stats?turn on statistics at bottom of screen.]"
+USAGE+="[C:continue]:[file?continue from previous data stream file (default is to start new data stream).]"
+
+##
+## configuration settings
+##
+# option flags
+OPT_CHIP=0
+OPT_SENSOR=0
+OPT_WIDTH=0
+OPT_TITLE=0
+OPT_STATS=0
+OPT_CONT=0
+# variables
+CHIP=
+SENSOR=
+WIDTH=120
+TITLE=
+DATALOG=./sensors-data.log
+typeset -f STAT_max=
+typeset -f STAT_min=
+typeset -f STAT_avg=
+typeset -f STAT_last=
+GNUPLOT_INPUT=
+# external dependencies
+typeset -A UTILS
+UTILS[rm]=$(which rm 2>/dev/null)
+UTILS[grep]=$(which grep 2>/dev/null)
+UTILS[awk]=$(which awk 2>/dev/null)
+UTILS[tail]=$(which tail 2>/dev/null)
+UTILS[head]=$(which head 2>/dev/null)
+UTILS[sort]=$(which sort 2>/dev/null)
+UTILS[wc]=$(which wc 2>/dev/null)
+UTILS[mktemp]=$(which mktemp 2>/dev/null)
+UTILS[sensors]=$(which sensors 2>/dev/null)
+UTILS[gnuplot]=$(which gnuplot 2>/dev/null)
+
+##
+## functions
+##
+
+#------------------------------------------------------------------------------
+# description: check availability of external utilities
+# inputs: (global) UTILS[*]
+# outputs: error messages to stdout
+# exit: 0=success, 1=failed
+#------------------------------------------------------------------------------
+function check_utils
+{
+    typeset key=
+    typeset -i err=0
+
+    for key in ${!UTILS[*]}
+    do
+       # if empty, means not in PATH
+       if [[ -z ${UTILS[$key]} ]] ; then
+           (( err++ ))
+           print -u2 "Error: could not find $key in your PATH."
+           print -u2 "Hint: maybe need to install ${key}?"
+           continue
+       fi
+
+       # check file exists and executable
+       if [[ ! -f ${UTILS[$key]} ]] ; then
+           (( err++ ))
+           print -u2 "Error: could not find file ${UTILS[$key]}."
+       fi 
+       if [[ ! -x ${UTILS[$key]} ]] ; then
+           (( err++ ))
+           print -u2 "Error: file ${UTILS[$key]} is not executable."
+       fi 
+    done
+    (( err == 0 )) && return 0 || return 1
+}
+
+#------------------------------------------------------------------------------
+# description: collect sensor data and stream to $_datalog
+# inputs: $1=chip, $2=sensor, $datalog
+# outputs: stream sensor data to $datalog
+# exit: none
+#------------------------------------------------------------------------------
+function collect_sensor_data
+{
+    typeset _chip=$1
+    typeset _sensor=$2
+    typeset _datalog=$3
+    typeset _reading=
+
+    while true
+    do
+        _reading=$(${UTILS[sensors]} -u $_chip \
+           | ${UTILS[awk]} -v sensor=$_sensor '$1==sensor":" {print $2}')
+        print $_reading >> $_datalog
+        sleep 1
+    done
+}
+
+##
+## main
+##
+
+SELF=$0
+PID=$$
+
+# check external dependencies
+check_utils || exit 1
+
+# process command line args
+while getopts "$USAGE" option
+do
+    case $option in
+       c|chip)     OPT_CHIP=1 && CHIP=$OPTARG      ;;
+       s|sensor)   OPT_SENSOR=1 && SENSOR=$OPTARG  ;;
+       w|width)    OPT_WIDTH=1 && WIDTH=$OPTARG    ;;
+       t|title)    OPT_TITLE=1 && TITLE=$OPTARG    ;;
+       S|stats)    OPT_STATS=1                     ;;
+       C|continue) OPT_CONT=1 && DATALOG=$OPTARG   ;;
+    esac
+done
+
+if (( OPT_CONT == 0 )) ; then
+    DATALOG=$(${UTILS[mktemp]} ./sensor-data.XXXXX)
+fi
+# check DATALOG is writable
+if [[ ! -w $DATALOG ]] ; then
+    print -u2 "Error: cannot write to $DATALOG"
+    exit 1
+fi
+
+# fork collect_sensor_data() in background
+collect_sensor_data $CHIP $SENSOR $DATALOG &
+COLLECTOR_PID=$!
+trap "kill $COLLECTOR_PID && print \"\nsensor data=$DATALOG\"" EXIT
+
+# wait for $DATALOG to have data
+while (( $(${UTILS[wc]} -l <$DATALOG) == 0 ))
+do
+    sleep 1
+done
+
+while true
+do
+    # get size of terminal
+    read rows cols < <(stty size)
+    # if OPT_STATS=1, reduce rows by 3 for space to print max, min, avg, last.
+    if (( OPT_STATS == 1 )) ; then
+       (( rows -= 3 ))
+    fi
+    GNUPLOT_INPUT="set terminal dumb size $cols, $rows;"
+
+    # if $STAT_max is not empty, base yrange max on 120% of STAT_max
+    if [[ -n $STAT_max ]] ; then
+       (( yrange_max = STAT_max * 1.20 ))
+       GNUPLOT_INPUT+="set yrange [0:$yrange_max];"
+    else
+       GNUPLOT_INPUT+="set yrange [0:*];"
+    fi
+
+    # if TITLE is not empty, set it
+    if [[ -n $TITLE ]] ; then
+       GNUPLOT_INPUT+="set title \"$TITLE\";"
+    fi
+
+    # wrap up final bits of GNUPLOT_INPUT
+    GNUPLOT_INPUT+="set nokey; plot '-' with lines"
+
+    ${UTILS[tail]} -$WIDTH $DATALOG \
+       | ${UTILS[gnuplot]} -e "$GNUPLOT_INPUT"
+
+    # if OPT_STATS=1, print stats at bottom
+    if (( OPT_STATS == 1 )) ; then
+       # get last value
+       STAT_last=$(${UTILS[tail]} -1 $DATALOG)
+       # sort, and get max/min/avg
+       SORTED_DATA=$(${UTILS[mktemp]} /var/tmp/sensors-plotter.XXXXX)
+       ${UTILS[sort]} -n $DATALOG > $SORTED_DATA
+       STAT_min=$(${UTILS[head]} -1 $SORTED_DATA)
+       STAT_max=$(${UTILS[tail]} -1 $SORTED_DATA)
+       STAT_avg=$(${UTILS[awk]} '{ total += $1; count++ } END { print total/count }' $SORTED_DATA)
+       printf "\tlast = %f, max = %f, min = %f, avg = %f\n" $STAT_last $STAT_max $STAT_min $STAT_avg
+       ${UTILS[rm]} -f $SORTED_DATA
+    fi
+    
+    sleep 1
+done
+