=begin
 ******************************************************************************
 * Copyright 1990-2009 MBARI
 * MBARI Proprietary Information. All rights reserved.
 ******************************************************************************
 * Summary  : Interface to HESC-SERD utilities
 * Filename : hesc.rb
 * Author   : Henthorn
 * Project  : Benthic Rover
 * Version  : 1
 * Created  : Nov 09
 * Modified : 
 ******************************************************************************
=end

# Refer to Rover-code home directory as base
#
require "#{ENV['ROVER_HOME']}/utils/rover_environment"
#

require 'utils/datalog'
require 'utils/rover_thread'


##########
# Hesc driver implements interface to HESC-SERD board used for
# temporarily cutting power to Rover electronics
#
class Hesc
  include SyslogWriter


  MinDuration = 60        # At least 1 minute
  MaxDuration = 12*60*60  # No more than 12 hours

  #####
  #
  #
  def initialize(dev="/dev/ttyAM0")
    @dev = dev
    @attempts = 3

    syslog("_ Hesc object instantiated with dev #{@dev}")
  end
  #####


  NoComms     = "no comms"
  BadResponse = "bad response"

  #####
  # Perform basic check-up. Return true if passed. False if not.
  #
  def check_up
    ret = nil

    ##
    # Use 'safe' exit_ram command to check comms.
    # 
    t = RoverThread.new do
      unless (self.ver())
        ret = BadResponse
      end
    end
    sleep(5)

    if (t.alive?)
      t.kill
      t.join
      ret = NoComms
    end

    ret
  end
  #####

  
  #####
  # Initiate hibernation sequence, duration in seconds
  #
  def hibernate_sequence(duration)

    duration = duration.to_i
    if (duration < MinDuration ||
        duration > MaxDuration)
      syslog("_ invalid duration: #{duration}")
      return false
    end

    ##
    # Track the progress of the sequence.
    # We must not leave the HESC-SERD in calibration mode!
    #
    success = calibration_mode = false

    if (enter_ram())
      calibration_mode = true           # We've entered a delicate state, here

      if (index_ram())
        if (set_interval(duration))
          if (exit_ram())
            calibration_mode = false    # We're now out of trouble
            if (startup())
              ##
              # If we got to this point, we're hibernating
              #
              success = true
              syslog("_ hibernation sequence successful, goodnight...")
            end
          end
        end
      end
    end

    while (calibration_mode)      # Can't leave the HESC in calibration mode 
      sleep(0.5)
      calibration_mode = false if (exit_ram())
    end

    success

  end
  #####

  #####
  # Print the version number
  #
  def ver
    hesc_cmd('ver')
  end
  #####


  #####
  # Tell the HESC board to cut power now and supply
  # power again after the interval.
  #
  def startup()
    hesc_cmd('startup')
  end
  #####



  #------------------- private methods -----------------

  #####
  # Enter RAM access mode on HESC board
  #
  def enter_ram()
    hesc_cmd('enter_ram')
  end
  #####


  #####
  # Set the RAM index pointer
  #
  def index_ram()
    hesc_cmd('index_ram')
  end
  #####


  #####
  # Set hibernation duration in seconds.
  # Write duration value into RAM.
  #
  def set_interval(duration)
    hesc_cmd("set_interval -t #{duration}")
  end
  #####


  #####
  # Exit RAM access mode
  #
  def exit_ram()
    hesc_cmd('exit_ram')
  end
  #####


  #####
  # Make attempts to execute a command.
  #
  def hesc_cmd(cmd)

    for i in 1..@attempts
      ret = false
      
      ##
      # Execute cmd in separate thread because it will hang
      # if the HESC is not communicating.
      #
      t = RoverThread.new do
        syslog("_ executing #{cmd}")
        ret = true if (system(cmd))
      end
      
      ##
      # Give it a second to run
      #
      sleep(1)
      
      ##
      # Kill the thread if it's still alive and continue
      # 
      if (t.alive?)
        syslog("! command thread still running")
        t.kill
        ret = false
      end
      t.join
      
      ##
      # Try again unless it worked
      #
      next unless(ret)
      break
    end

    syslog("! #{cmd} failed") unless ret
    ret
  end
  #####


end
##########


##########
# SimHesc
#
class SimHesc < Hesc

  #####
  def ver
    syslog("_ simulating Hesc.ver")
    true
  end
  #####

  #####
  def hesc_cmd(cmd)
    syslog("_ simulating #{cmd}")
    true
  end
  #####

end


def Hesc.test

  if (ARGV.length > 0)
    hesc = SimHesc.new
  else
    hesc = Hesc.new
  end

  puts("HESC firmware version   : #{hesc.ver()}")
  puts("Enable RAM access mode  : #{hesc.enter_ram()}")
  puts("Set RAM index pointer   : #{hesc.index_ram()}")
  puts("Write hibernate duration: #{hesc.set_interval(160)}")
  puts("Exit RAM access mode    : #{hesc.exit_ram()}")
end


##
# Standalone unit test (recommended)
# Include at the end of the file as a hook to run a unit test of
# the code from the command line (e.g., "$ ruby  my_class.rb")
#
if __FILE__ == $0
  # Test code here
  Hesc.test
end
