#--
#******************************************************************************
#* Copyright 1990-2007 MBARI
#* MBARI Proprietary Information. All rights reserved.
#******************************************************************************
#* Summary  : Real and simulated drivers for the Aanderaa optodes
#* Filename : aanderaa.rb
#* Author   : Henthorn
#* Project  : Benthic Rover
#* Version  :
#* Created  : Dec-06   Basic functions incl logging
#* Modified : Sept-07  Uses configuration manager, updated logging
#******************************************************************************
#++

require 'rover_environment'
require 'rover_utils'
require 'rover_thread'
require 'configman.rb'
require 'posix/serialport'

# Implements a driver/logger function for Aanderaa optodes. The optodes are
# set-up to emit data at a desired interval (e.g., 10 secs). The driver simply
# does a blocking read on the port, then reads, parses, and logs a line of data
# when one becomes available. 
#
class Aanderaa 
  include LogHelper

  # The _name_ attribute is derived from the portname when the object is
  # created. A portname of '/dev/optode_port' renders a name of 'optode_port'.
  # Portnames must be unique.
  #
  attr_reader :rawdata, :oxy, :sat, :temp, :name, :thread

  # Args: SerialPort config object, serial port name.
  # Typical usage: op = Aanderaa.new(nil, '/dev/this_optode')
  # Default serial port configuration: 9600,8,N,1
  #
  def initialize serial_config_obj, portname = '/dev/ref_optode'
    unless serial_config_obj
      serial_config_obj = SerialPort::Configuration.default
      serial_config_obj.baud = 9600
    end

    @dev = Posix::SerialPort.new portname, serial_config_obj
    @name = portname[(portname.rindex("/")+1)..(portname.length-1)]
    @model = "Optode"
    @sn = "xyz123"

    # The logfile name is derived from the portname as well
    @logfilename = @name + ".csv"

    # Distinguish between the multiple Aanderaa optodes on the vehicle
    # Model and S/N should come from ConfigMan singleton object
    #
    cm = ConfigMan.instance
    if cm
      @model = "Optode" unless @model = cm.get(@name, "model")
      @sn    = "xyz123" unless @sn    = cm.get(@name, "sn")
    end

    # Initialize the object variables and constants
    @header_written = false
    @oxy  = -1
    @sat  = 0
    @temp = 0
    @thread = nil
    @RAWDATA_ELEMENTS = 9
    
  end

  # Set the log directory. This is usually called
  # before each incubation to keep optode data
  # from different incubations apart.
  #
  def set_log_directory(dirname)
    return unless dirname

    newdir = LogDir.instance.datalog_dir + dirname
    Dir.mkdir(newdir) unless File.exists?(newdir)

    @logfilename = dirname + "/" + @name + ".csv"
    write_header
  end

  def write_header
    # Write the header for the log file
    datawrite("Rover Timestamp (UTC) , Mission Time (sec), Oxygen (uM) , " <<
              " Oxygen Saturation (%) , " <<
              " Water Temp (Celcius), #{@name}  #{@model}  S/N #{@sn} \n, \n",
	      @logfilename)
    @header_written = true
  end

  # Stop the read/log thread by killing it. Set :thread to nil
  #
  def stop
    if self.is_running?
      @thread.kill if @thread
      @thread = nil
      syslog("_ Stopping #{@name} optode thread")
    end
  end

  # Close the optode data port. Stop/kill the read/log thread first
  #
  def close
    self.stop
    syslog("_ Closing serial device for #{@name}")
    @dev.close
  end
  
  # Perform a blocking read on the optode data port. Read a line of data, 
  # parse, and log the data elements. The optode firmware setup controls
  # the rate of data flow.
  #
  def acquire_data
    # Aanderaa simply returns data string
    #
    #@dev.purge
    @rawdata = @dev.gets
    
    # Process data
    #
    @data = @rawdata.split
    
    if @data.length == @RAWDATA_ELEMENTS
      @oxy  = @data[4].to_f
      @sat  = @data[6].to_f
      @temp = @data[8].to_f
    elsif @data.length == 6
      @oxy  = @data[3].to_f
      @sat  = @data[4].to_f
      @temp = @data[5].to_f
    else
      syslog("! Error: #{@name}, @data array length is: " +
             "#{@data.length} and should be #{@RAWDATA_ELEMENTS}")
      elements_string = "["
      @data.each {|el| elements_string += el + ", "}
      elements_string += "]"
      syslog("_ elements in @data array: #{elements_string}")
      return false
    end
        
    syslog("_ Obtained optode data #{@name}: " +
          "oxy=#{@oxy}; sat=#{@sat}; temp=#{@temp}")
    return true
  end

  # Write data to log file
  #
  def log_data
    datalog("#{@oxy},#{@sat},#{@temp}", @logfilename)
  end

  # Read and log data in a separate thread
  #
  def run
    syslog("_ Running optode thread for #{@name}")
    write_header unless @header_written
    @thread = RoverThread.new do
      Thread.current["name"] = @name
      while true
        log_data if acquire_data
      end
    end
  end

  # Indicates whether read/log thread is running
  def is_running?
    if @thread
      return @thread.alive?
    else
      return false
    end
  end
  
end

# Overrides data read/log functions to provide simulation capability
#
class SimAanderaa < Aanderaa 
  include LogHelper
  
  def initialize config, portname = '/dev/ref_optode'
    #pass the real portname to super so that the @name variable gets
    #initialized
#    super(config, portname)

    # Initialize the object variables and constants
    @name = portname[(portname.rindex("/")+1)..(portname.length-1)]
    @rawdata = ""
    @oxy  = 1
    @sat  = 2
    @temp = 3
    @thread = nil
    @RAWDATA_ELEMENTS = 9
    @logfilename = @name + ".csv"
  end

  # The data reader must simulate the data interval provided by the optode
  #  
  def acquire_data
    Rover.instance.rt.rSleep(10)
    return true
  end
  
  def log_data
    datalog("#{@oxy},#{@sat},#{@temp}", @logfilename)
  end
  
end
