#!/bin/bash
#  not maemo specific.
#
# usage: u1233 READ?
# This script takes commands like SYST:BATT? or FETC? as parameter 
#  and fetches the result from KeySight U1233A via U1173A USB/IR adapter (ttyUSB0) 
#
self=`basename $0`
copyleft="(C) 2016-11-12 J.Reiseweber, GPL-V2"
version="0.6"

help(){
cat <<EOHELP

$self Version: $version. $copyleft
IR-adapter driver for KeySight U1233 DMM

Usage: $self [option...] -c <int> [option...] cmd...  # run a series of cmds in a time schedule, format and display results
       $self cmd        # send single cmd to DMM and display result on stdout
       $self -h         # show this helptext

    options:
    -i <int>        : \$interval in centiseconds (0.01s) 
    -c <int>        : \$count, continuous sampling for <int> samples, 0=infinite
    -n              : \$print_n - print count index left to result (starts at 1)
    -N \"<str>\"    : \$del0 - separator/delimiter after count index, defaults to value of-D
    -d \"<str>\"    : \$dateformat of timestamp right to result, as in (1)date +format
    -D \"<str>\"    : \$del - separator/delimiter in between results and between last result and timestamp, defaults to \"; \"
    -S \"<str>\"    : \$starttime - of the virtual sample with index0. Default: EPOC + N x <interval>, most recent
    -2 \"<cmd>\"    : # secondary command(s) to execute immediately after all primary cmds but print result to stderr. May occcur multi
    -E \"<str>\"    : # \$errtext to output in case of read error, instead of <result>. When empty, aborts on error
    -h              : help
      $<word> denotes an ENV var $self will use when provided. $self -D "/" ==  del=/ $self. Options override ENV vars
    
EOHELP
}


#set -e -u

# user config
###IRdrv=/sys/bus/usb-serial/drivers/pl2303;
VIDPID='PRODUCT=67b/2303/300'
timeout=0.5





initialize(){
  self=`basename $0`
  
  # find device based on name of kernel driver for IR adapter
#  dev=/dev/$(basename ${IRdrv}/ttyUSB*)
  dev=$(for f in /sys/bus/usb/devices/usb1/*/*/*/device/uevent; do grep -q $VIDPID $f && basename `dirname $f`/*/ttyUSB*; done | sort | uniq)
  if [ "${dev/ttyUSB[0-9]/OK}" != "OK" ]; then
    echo -e "${self}: Searching for $VIDPID: found \n$dev\nABORTING..."  >&2
    exit 5
  fi

  #why exec? cruft for the simple version, needed to keep filehandle open when we add loops etc
  exec 4>/dev/$dev 5</dev/$dev
  stty 1:0:cbd:0:3:1c:7f:15:4:5:1:0:11:13:1a:0:12:f:17:16:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 <&5 #|| ( ls -l /dev/$dev >&2; exit 10 )

  #prolly cruft, and didn't help flush the buffer when some other process keeps the file handle open (exec >/dev/bla)
  read -t 0 void <&5
}



ident(){
  DMMID=`docmd "*IDN?"`;
  case $DMMID in
    Agilent\ Technologies\,U1233A*)
	#echo "$DMMID";
	return;
	;;
    '')
	echo "$self: DMM off? *IDN returns \"$DMMID\""
	return 4;
	;;
    *)
	echo "$self: Wrong DMM? *IDN returns \"$DMMID\""
	return 3;
	;;
  esac;
}



docmd() {
  (read -t $timeout result <&5 &&
  echo -n "${result: +0:-1}")&
  echo "$@" >&4
  wait
}





################## main #####################
main(){


  #parseoptions
  OPTIONS="i:c:nN:d:D:S:2:E:h"
  #i
  : ${interval:=100}; 	#centiseconds, 100=1s
  #c
  : ${count:= -1};	#how many loops, 0=inf, -1=terse_oneshot
  #n print_n=''
  #N
    #deferred ${del0:=$del}
  #d
  : ${dateformat:=%H:%M:%S.%N}
  #D
  : ${del:=; }
  #S
  t0="`date +%s%2N`"; #realtime starttime
  : ${starttime:=$(( $t0 / interval * interval ))}; # we date back display-starttime to last 'plain second' event in interval schedule assuming running from epoch
  #2
   #TODO
  #E
   #TODO
  #h help
  
  while getopts "$OPTIONS" Option
  do
	  case $Option in
		  i ) interval="$OPTARG";;
		  c ) count="$OPTARG";;
		  n ) print_n=true;;
		  N ) del0="$OPTARG";;
		  d ) dateformat="$OPTARG";;
		  D ) del="$OPTARG";;
		  S ) starttime="$OPTARG";;
		  2 ) echo "$Option not inmplemented yet" >&2
		      exit;;
		  E ) echo "$Option not inmplemented yet" >&2
		      exit;;
		  h ) help
		      exit;;
		  * ) help
		      exit $ERROR_OPT;;
	  esac
  done

  : ${del0:=$del}

  shift $(($OPTIND - 1)); #now commands


  
  initialize

  if [ $count -ge 0 ] ; then
    # the simple approach like:
    #  while echo "`docmd "$1"`; `date +%H:%M:%S`"; do sleep 1; done;
    # obviously creates a loop of 1 + x seconds duration (x for runtime of cmds) and thus skips a second every now and then, so we do a lil fancy here

    ident||exit $?;

    for (( nsdelta=0, n=1;  n<=count || count==0;  n++ ));  do 
      # calculate sleep time til next exact point in schedule
      while :; do
        nsdelta=$(( ((t0 + n * interval) * 10000000)  + 1000000000000000000  )); ds=$(( `date +%s%N` )); # +1000000000000000000 for leading zeroes, 2 less than max bash allows
#echo -e "1:: $nsdelta :: ${nsdelta: +1:-9}.${nsdelta: -9}::\n*:: $ds ::" >&2
	[ $(( nsdelta - ds )) -lt 1000000000000000000 ] && echo "Skipping a sample..." && continue 2;
        nsdelta=$(( nsdelta - ds )); # +1000000000000000000 for leading zeroes, 2 less than max bash allows
#echo "2:: $nsdelta  :: ${nsdelta: +1:-9}.${nsdelta: -9}::" >&2
        # read unsolicited responses during waiting - instead of the former 'sleep "${nsdelta: +0:-9}.${nsdelta: -9}";'
        if read -t "${nsdelta: +1:-9}.${nsdelta: -9}" unsolresult <&5; then
          echo  "unsolicited: ${unsolresult: +0:-1}" >&2 # TODO: handle unsol resp in a smarter way than that
        else
          break
        fi
      done
      [ -v print_n ] && echo -n "${n}${del0}"
      for cmd in $@; do
        echo -n "`docmd "$cmd"`${del}"
      done
      tdispnow=$(( ((starttime + n * interval) * 10000000)  + 1000000000000000000 )); # +1000000000000000000 for leading zeroes, 2 less than max bash allows
#echo ">>@$starttime :: $n ::  $(( starttime + n * interval )) :: $tdispnow<<" >&2
      tdispnow="${tdispnow: +1:-9}.${tdispnow: -9}"
#echo ">>@$starttime :: $n ::  $(( starttime + n * interval )) :: $tdispnow<<" >&2
      date "+${dateformat}" -d "@$tdispnow"
    done
  ##### single command  
  else
    docmd "$1";
    echo
  fi;
}
main "$@"