#!/usr/bin/ruby
#
=begin
/****************************************************************************/
/* Copyright (c) 2018 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  : Utility to feed watchdog on tether controller as long as      */
/*            there is connectivity to a shore gateway.                     */
/* Filename : dogsitter.rb                                                  */
/* Author   : Henthorn                                                      */
/* Project  : Wave Energy Buoy                                              */
/* Version  : 1.0                                                           */
/* Created  : 09/26/2018                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
Example:  ruby dogsitter.rb --host atlas.shore.mbari.org --loghome /logs
=end

require 'getoptlong'

$verbose    = false
$start_time = Time.new

def usage()
   puts(
      "dogsitter.rb  Utility to feed tether controller watchdog. Continue\n" \
      "              feeding while shore host is reachable on the network.\n"\
      "Usage:\n"                                                             \
      " ruby dogsitter.rb  HOST  [OPTION]\n"                                 \
      "   -h  --host=HOST\n"                                                 \
      "          Required argument: shore hostname or ip.addr to ping\n"     \
      "   -l  --loghome=directory\n"                                         \
      "          Directory to place the CSV log file. Default is \".\".\n"   \
      "   -t  --time=N\n"                                                    \
      "          Seconds to delay between pings. Default is 600.\n"          \
      "   -n  --noop\n"                                                      \
      "          Do not send watchdog command, otherwise normal operation.\n"\
      "   -v  --verbose\n"                                                   \
      "          Chatty."
       )
   exit
end

##
## @brief      Use system ping command to detect host
##
## @param      hostname  The name or ip of the host on shore
##
## @return     Returns true if the host is reachable, otherwise false
##
def ping_host(hostname)
   #
   # Ping the host using the shell command, capturing the result.
   # One ping, one ping only.
   # 
   pa = []
   ping = `ping -c 1 #{hostname}`
   puts (ping) if ($verbose)
   #
   # Split the result on commas. The number of received responses
   # to the ping will be in the second element of the array, e.g.,
   # "1 received"
   # 
   pa = ping.split(',') if (ping)
   if (pa.length > 1)
      puts (pa[1]) if ($verbose)
      #
      # Split the received element on whitespace. The number received is
      # in the first element. Return true if that number is 1.
      # 
      recv = pa[1].split
      return true if (recv[0].to_i == 1)
   end
   # 
   # Return false on any other condition.
   # 
   return false
end

##
## @brief      Feed the watchdog
##
## @return     nada
##
def feed_dog()
   r = `tf_WatchDog`
   return
end

##
## @brief      Return the arguments and options from the command line.
##             On error, calls usage() which exits the program.
##
## @return     Returns the parameters used in the main routine:
##             hostname or ip of controller with the watchdog
##             hostname or ip of the shore gateway
##             delay time in seconds between feedings
##
def get_options()
   # Defaults
   host  = noop = $verbose = nil
   logs  = "."    # Place log file in local directory
   delay = 600    # 10 minutes between pings

   begin
      opts = GetoptLong.new(
         ["--host",    "-h", GetoptLong::REQUIRED_ARGUMENT],
         ["--loghome", "-l", GetoptLong::REQUIRED_ARGUMENT],
         ["--time",    "-t", GetoptLong::REQUIRED_ARGUMENT],
         ["--noop",    "-n", GetoptLong::NO_ARGUMENT],
         ["--verbose", "-v", GetoptLong::NO_ARGUMENT]
         )

      opts.each do | opt, arg |
         if (opt ==    "--host")
            host = arg
         elsif (opt == "--loghome")
            logs = arg
         elsif (opt == "--time")
            delay = arg.to_i
         elsif (opt == "--noop")
            noop = true
         elsif (opt == "--verbose")
            $verbose = true
         end
      end

   rescue GetoptLong::InvalidOption
      usage()
   end
   # 
   # User must specify a host to ping
   # 
   unless (host)
      puts("Error: A hostname or ip.addr is required.\n\n")
      usage()
   end

   return host, logs, delay, noop, $verbose
end

##
## @brief      Create a new log file, write the header, return the File object
##
## @param      logdir  The log directory
## @param      host    The hostname or ip
## @param      delay   The delay between pings
## @param      noop    The noop param
##
## @return     { A File object opened and ready for writing }
##
def open_log(logdir, host, delay, noop)
   #
   # If the directory does not exist, open the file at the cwd
   # 
   unless (File.directory?(logdir))
      logdir = "."
   end
   feed = noop ? 0 : 1
   #
   # Create a unique file name by appending an integer to the end.
   # Loop until the filename does not exist.
   # 
   count = 1
   basename = "watchdog.csv"
   filename = logdir + "/" + basename
   while (File.exists?(filename)) 
      filename = logdir + "/" + basename + "." + count.to_s.rjust(3, "0")
      count = count + 1
   end
   #
   # Create the file, write the header, return the file.
   # 
   f = File.open(filename, File::CREAT | File::RDWR, 0644)
   f.write("Timestamp, Elapsed Time, Reachable, Host=#{host}, Delay=#{delay}, Feed=#{feed}\n")
   return f
end

##
## @brief      Write a log record to the log file
##
## @param      logfile    The logfile
## @param      reachable  True if the host is reachable, otherwise false
##
## @return     { the logfile object }
##
$reached = nil
def log_write(logfile, reachable)
   #
   # Write a log record only if reachable changed state
   # 
   if (reachable != $reached)
      $reached = reachable
   else
      return
   end

   now = Time.now
   t = now.strftime("%Y%m%dT%H:%M:%SZ")
   e = now.to_i - $start_time.to_i
   if (logfile)
      logfile.write("#{t}, #{e}, #{reachable}\n")
      logfile.flush()
   end

   logfile
end

##
# Main script. Begin by parsing the command line.
#
shore_host, logs, delay, noop, verbose = get_options()

#
# Create a log file and enter the main loop
#
logfile = open_log(logs, shore_host, delay, noop)

#
# Loop forever
# 
reachable = false
while (true) do
   #
   # If the host is reachable, feed the watchdog
   # 
   if (ping_host(shore_host))
      feed_dog() unless (noop)
      reachable = true
      puts("Successfully fed the dog") if ($verbose)
   else
      reachable = false
      puts("#{shore_host} unreachable") if ($verbose)
   end
   #
   # Write a record and wait until the next ping time
   # 
   log_write(logfile, reachable)
   sleep(delay)
end
