######################## modem.rb  henthorn@mbari.org ##########################
#
#
################################################################################

require 'rover_environment'
require 'rover_utils'
require 'posix/serialport'

# Benthos Modem commands and responses
#
$VERBOSE_LEVEL = "ATS13=00\r"
$WRITE_DELAY =   "ATS8=10\r"
$WAIT_TIME =     "ATS7=10\r"
$DOPPLER =       "ATS1=1\r"
$IDLE_TIME =     "ATS10=36\r"
$CMD_MODE =      "+++\r"
$ONLINE_MODE =   "ATO\r"
$CR_LF =         "\xd\xa"

$CMD_MODE_CONF = ">"
$ONLINE_CONF =   "bits/sec\xd\xa"
$CMD_CONF =      "OK\xd\xa>"
  

############################################################################
# Benthos Modem driver
#
class BenthosModem
  include LogHelper
  
  attr_reader :dev
  
  def initialize config, portname='/dev/modem' 
      unless config
        config = SerialPort::Configuration.default
        #default baud is 38.4k, let's change it to 9600
        config.baud = 9600
      end
      
    @dev = Posix::SerialPort.new portname, config
    @dev.sync=true
    @cmdMode = false
  end

  # Set up a modem object for Rover
  #
  def BenthosModem.rover
    m = BenthosModem.new nil, '/dev/modem'
  end 

  # Initialize modem
  #
  def init
    return false unless self.commandMode
    
    # Set verbose level, delays, and doppler correction
    #
    return false unless command($VERBOSE_LEVEL)
    return false unless command($WRITE_DELAY)
    return false unless command($WAIT_TIME)
    return false unless command($DOPPLER)
    return false unless command($IDLE_TIME)

    # Set to onlineMode, ready for data transfer
    #
    return false unless self.onlineMode

    return true    
  end

  # Close the port
  #
  def close
    @dev.close
  end

  def commandMode?
    @cmdMode
  end
  
  # Set the modem to command mode, and confirm
  #
  def commandMode
    syslog("_ Modem - setting command mode from #{@cmdMode}")
    @dev.write($CMD_MODE)
    if nil == self.confirm($CMD_MODE_CONF)
      @cmdMode = true
    else
      syslog("! Modem - setting command mode failed")
      @cmdMode = false
    end
    return @cmdMode
  end
  
  # Send a modem command to the modem, confirm that
  # command was carried-out
  #
  def command(cmdstr)
    commandMode unless @cmdMode
    syslog ("_ Modem - command \"#{cmdstr}\"")
    @dev.write(cmdstr)
    if  nil == self.confirm($CMD_CONF)
      return true
    else
      syslog ("!Modem - command (#{cmdstr}) unsuccessful")
      return false
    end
  end
  
  # Set the modem to online mode, and confirm
  #
  def onlineMode
    readData(1)   # Clear the read buffer
    
    unless @cmdMode
      commandMode
    end
    
    unless @cmdMode
      syslog("! Modem - Unable to command modem to online mode")
      return false
    end
    
    @dev.write($ONLINE_MODE)
    resp = self.confirm($ONLINE_CONF)
    if resp == nil
      @cmdMode = false
    elsif resp == $CR_LF
      @cmdMode = false
    end
    return !@cmdMode
  end

  # Send a packet to the listening modem
  #
  def write(packet)
    syslog("_ Modem transferring packet: #{packet}")
    @dev.write(packet) unless @cmdMode
  end

  # Read string from modem and return it, timeout in seconds
  #
  def readString(timeout=2)
    a = Array.new(1,@dev)
    str = String.new("")
    while(IO.select(a, nil, nil, timeout))
      str << @dev.getc
    end
    if str.length > 0
      syslog("_ Modem received packet: #{str}")
    end
    return str
  end
  
  def readData(timeout=2)
    self.readString(timeout)
  end
  
  
  # Read response from modem and confirm against expected response.
  # The confirm string will be compared to the LAST thing in the read buffer.
  # Return nil if equal, otherwise return the actual response.
  #
  def confirm(confstr)
    clen = confstr.length
    response = self.readString
    
    # Compare the confirm string to the last part of the response
    #
    token = response.slice(0-clen, clen)
    if token == confstr
      return nil
    else
      syslog("!Modem - command not confirmed. Expected (#{confstr}) but recv'd (#{token})")
      return token
    end
  end

  # Check to see if there is any data waiting to be read
  #
  def dataPresent?
    a = Array.new(1,@dev)
    return (nil != IO.select(a, nil, nil, 0.1))
  end
  
end
