
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 Dallas Semiconductor Corporation, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dallas Semiconductor
 * shall not be used except as stated in the Dallas Semiconductor
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

package com.dalsemi.onewire.container;

// imports
import com.dalsemi.onewire.utils.*;
import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;


/**
 *  The DS2438 Smart Battery Monitor provides several functions that are desirable
 *  to carry in a battery pack: a means of tagging a battery pack with a unique
 *  serial number, a direct-to-digital tmperature sensor which eliminates the need
 *  for thermistors in the pack, an A/D converter which measures the battery
 *  voltage and current, an integrated current accumulator which keeps a running
 *  total of all current going into and out of the battery, an elapsed time meter,
 *  and 40 bytes of nonvolatile EEPROM memory for storage of important parameters.
 *
 *  Note: Sometimes the VAD input will report 10.23 V even if nothing is attached.
 *  This value is also the maximum voltage that part can report.
 *
 *  @version    0.00, 28 Aug 2000
 *  @author     COlmstea
 *
 */
public class OneWireContainer26
   extends OneWireContainer
   implements ADContainer, TemperatureContainer, ClockContainer
{

   /**
    * Memory commands.
    */
   private static final byte READ_SCRATCHPAD_COMMAND  = ( byte ) 0xBE;
   private static final byte RECALL_MEMORY_COMMAND    = ( byte ) 0xB8;
   private static final byte COPY_SCRATCHPAD_COMMAND  = ( byte ) 0x48;
   private static final byte WRITE_SCRATCHPAD_COMMAND = ( byte ) 0x4E;
   private static final byte CONVERT_TEMP_COMMAND     = ( byte ) 0x44;
   private static final byte CONVERT_VOLTAGE_COMMAND  = ( byte ) 0xB4;

   /**
    * Channel selector for the VDD input.  Meant to be used with
    * a battery.
    */
   public static final int CHANNEL_VDD = 0x00;

   /**
    * Channel selector for the VAD input.  This is the general purpose
    * A-D input.
    */
   public static final int CHANNEL_VAD = 0x01;

   /**
    * Flag to set/check the Current A/D Control bit with setFlag/getFlag. When
    * this bit is true, the current A/D and the ICA are enabled and
    * current measurements will be taken at the rate of 36.41 Hz.
    */
   public static final byte IAD_FLAG = 0x01;

   /**
    * Flag to set/check the Current Accumulator bit with setFlag/getFlag. When
    * this bit is true, both the total discharging and charging current are
    * integrated into seperate registers and can be used for determining
    * full/empty levels.  When this bit is zero the memory (page 7) can be used
    * as user memory.
    */
   public static final byte CA_FLAG = 0x02;

   /**
    * Flag to set/check the Current Accumulator Shadow Selector bit with
    * setFlag/getFlag.  When this bit is true the CCA/DCA registers used to
    * add up charging/discharging current are shadowed to EEPROM to protect
    * against loss of data if the battery pack becomes discharged.
    */
   public static final byte EE_FLAG = 0x04;

   /**
    * Flag to set/check the voltage A/D Input Select Bit with setFlag/getFlag
    * When this bit is true the battery input is (VDD) is selected as input for
    * the voltage A/D input. When false the general purpose A/D input (VAD) is
    * selected as the voltage A/D input.
    */
   public static final byte AD_FLAG = 0x08;

   /**
    * Flag to check whether or not a temperature conversion is in progress
    * using getFlag().
    */
   public static final byte TB_FLAG = 0x10;

   /**
    * Flag to check whether or not an operation is being performed on the
    * nonvolatile memory using getFlag.
    */
   public static final byte NVB_FLAG = 0x20;

   /**
    * Flag to check whether or not the A/D converter is busy using getFlag().
    */
   public static final byte ADB_FLAG = 0x40;

   /**
    * Holds the value of the sensor resistance.
    */
   private double Rsens = .05;

   //--------
   //-------- Constructors
   //--------

   /**
    * Default constructor
    */
   public OneWireContainer26 ()
   {
      super();
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer26 (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      super(sourceAdapter, newAddress);
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer26 (DSPortAdapter sourceAdapter, long newAddress)
   {
      super(sourceAdapter, newAddress);
   }

   /**
    * Create a container with a provided adapter object
    * and the address of the iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    * this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public OneWireContainer26 (DSPortAdapter sourceAdapter, String newAddress)
   {
      super(sourceAdapter, newAddress);
   }

   /**
    *  Returns the Dallas Semiconductor part number of the iButton
    *  as a string.
    *
    *  @return <code>String</code> representation of the iButton name
    *
    */
   public String getName ()
   {
      return "DS2438";
   }

   /**
    *  Return the alternate Dallas Semiconductor part number or name.
    *  ie. Smart Battery Monitor
    *
    *  @return <code>String</code> representation of the alternate names.
    */
   public String getAlternateNames ()
   {
      return "Smart Battery Monitor";
   }

   /**
    *  Return a short description of the function of the iButton type.
    *
    *  @return <code>String</code> representation of the function description.
    */
   public String getDescription ()
   {
      return "1-Wire device that integrates the total current charging or "
             + "discharging through a battery and stores it in a register. "
             + "It also returns the temperature (accurate to 2 degrees celcius),"
             + " as well as the instantaneous current and voltage and also "
             + "provides 40 bytes of EEPROM storage.";
   }

   /**
    * Set the value of the sense resistor you are using to determine
    * battery current.  This value is used in the getCurrent() calculation.
    * See the DS2438 datasheet for more information on sensing battery
    * current.
    *
    * @param resistance Value of the sense resistor in Ohms.
    */
   public synchronized void setSenseResitor (double resistance)
   {
      Rsens = resistance;
   }

   /**
    * Get the value used for the sense resistor in the getCurrent()
    * calculations.
    *
    * @return Currently stored value of the sense resistor in ohms.
    */
   public double getSenseResistor ()
   {
      return Rsens;
   }

   /**
    * Reads the specified 8 byte page and returns the data in an array.
    *
    * @param <code>int</code> the page number to read.
    *
    * @param page
    *
    * @return <code>byte[]</code> containing the 8 bytes that make up the page.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public byte[] readPage (int page)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] buffer = new byte [11];
      byte[] result = new byte [8];
      int    crc8;   // this device uses a crc 8

      /* check validity of parameter */
      if ((page < 0) || (page > 7))
         throw new IllegalArgumentException("OneWireContainer26-Page " + page
                                            + " is an invalid page.");

      /* perform the read/verification */
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         /* recall memory to the scratchpad */
         buffer [0] = RECALL_MEMORY_COMMAND;
         buffer [1] = ( byte ) page;

         adapter.dataBlock(buffer, 0, 2);

         /* perform the read scratchpad */
         adapter.reset();
         adapter.select(address);

         buffer [0] = READ_SCRATCHPAD_COMMAND;
         buffer [1] = ( byte ) page;

         for (int i = 2; i < 11; i++)
            buffer [i] = ( byte ) 0x0ff;

         adapter.dataBlock(buffer, 0, 11);

         /* do the crc check */
         crc8 = CRC8.compute(buffer, 2, 9);

         if (crc8 != 0x0)
            throw new OneWireIOException(
               "OneWireContainer26-Bad CRC during read." + crc8);

         // copy the data into the result
         System.arraycopy(buffer, 2, result, 0, 8);
      }
      else
         throw new OneWireException("OneWireContainer26-device not found.");

      return result;
   }

   /**
    * Writes a page of memory to the DS2438. Pages 3-6 are always
    * available for user storage and page 7 is available if the CA bit is set
    * to 0 (false) with setFlag().
    *
    * @param <code>int</code> indicating the page number.
    * @param <code>byte[]</code> containing the 8 bytes of the page.
    * @param offset
    *
    * @param page
    * @param source
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void writePage (int page, byte[] source, int offset)
      throws OneWireIOException, OneWireException
   {
      byte[] buffer = new byte [10];

      /* check parameter validity */
      if ((page < 0) || (page > 7))
         throw new IllegalArgumentException("OneWireContainer26-Page " + page
                                            + " is an invalid page.");

      if (source.length < 8)
         throw new IllegalArgumentException(
            "OneWireContainer26-Invalid data page passed to writePage.");

      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         // write the page to the scratchpad first
         buffer [0] = WRITE_SCRATCHPAD_COMMAND;
         buffer [1] = ( byte ) page;

         System.arraycopy(source, offset, buffer, 2, 8);
         adapter.dataBlock(buffer, 0, 10);

         // now copy that part of the scratchpad to memory
         adapter.reset();
         adapter.select(address);

         buffer [0] = COPY_SCRATCHPAD_COMMAND;
         buffer [1] = ( byte ) page;

         adapter.dataBlock(buffer, 0, 2);
      }
      else
         throw new OneWireException("OneWireContainer26-Device not found.");
   }

   /**
    * Checks the specified flag in the status/configuration register
    * and returns its status as a boolean.
    *
    * @param <code>byte</code> containing the bitmask.
    * <p>Acceptable parameters: IAD_FLAG, CA_FLAG, EE_FLAG, AD_FLAG, TB_FLAG,
    * NVB_FLAG, ADB_FLAG.
    * (may be ORed with | to check the status of more than one).
    *
    * @param flagToGet
    *
    *
    * @return
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public boolean getFlag (byte flagToGet)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(0);

      if ((data [0] & flagToGet) != 0)
         return true;

      return false;
   }

   /**
    * Set one of the flags in the STATUS/CONFIGURATION register.
    *
    * @param <code>byte</code> with bitmask of the flag to set.
    * <p>Acceptable parameters: IAD_FLAG, CA_FLAG, EE_FLAG, AD_FLAG, TB_FLAG,
    * NVB_FLAG, ADB_FLAG.
    * @param <code>boolean</code> indicating the value of the flag to set.
    *
    * @param flagToSet
    * @param flagValue
    *
    * @throws OneWireIOException Error setting flag [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void setFlag (byte flagToSet, boolean flagValue)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(0);

      if (flagValue)
         data [0] = ( byte ) (data [0] | flagToSet);
      else
         data [0] = ( byte ) (data [0] & ~(flagToSet));

      writePage(0, data, 0);
   }

   /**
    * Get the instantaneous current. The IAD flag must be true!!
    * Remember to set the Sense resistor value using
    * setSenseResitor(double).
    *
    *
    * @param state
    * @return <code>double</code> containing the current in Amperes.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public double getCurrent (byte[] state)
   {
      short rawCurrent = ( short ) ((state [6] << 8) | (state [5] & 0x0ff));

      return rawCurrent / (4096.0 * Rsens);
   }

   /**
    * Calculate the remaining capacity in mAH as outlined in the data sheet.
    *
    * @return <code>double</code> containing the mAH of battery capacity remaining.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public double getRemainingCapacity ()
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      int ica = getICA();

      return (1000 * ica / (2048 * Rsens));
   }

   /**
    * Determines if the battery is charging and returns a boolean.
    *
    *
    * @param state
    * @return <code>boolean</code> indicating recharging status.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public boolean isCharging (byte[] state)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {

      // positive current (if the thing is hooked up right) is charging
      if (getCurrent(state) > 0)
         return true;

      return false;
   }

   /**
    * Calibrate the current ADC. Although the part is shipped calibrated,
    * calibrations should be done whenever possible for best results.
    * NOTE: You MUST force zero current through Rsens (the sensor resistor)
    * while calibrating.
    *
    * @throws OneWireIOException Error calibrating [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void calibrateCurrentADC ()
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data;
      byte   currentLSB, currentMSB;

      // grab the current IAD settings so that we dont change anything
      boolean IADvalue = getFlag(IAD_FLAG);

      // the IAD bit must be set to "0" to write to the Offset Register
      setFlag(IAD_FLAG, false);

      // write all zeroes to the offset register
      data     = readPage(1);
      data [5] = data [6] = 0;

      writePage(1, data, 0);

      // enable current measurements once again
      setFlag(IAD_FLAG, true);

      // read the Current Register value
      data       = readPage(0);
      currentLSB = data [5];
      currentMSB = data [6];

      // disable current measurements so that we can write to the offset reg
      setFlag(IAD_FLAG, false);

      // change the sign of the current register value and store it as the offset
      data     = readPage(1);
      data [5] = ( byte ) (~(currentLSB) + 1);
      data [6] = ( byte ) (~(currentMSB));

      writePage(1, data, 0);

      // eset the IAD settings back to normal
      setFlag(IAD_FLAG, IADvalue);
   }

   /**
    * Set the minimum current measurement magnitude for which the ICA/CCA/DCA
    * are incremented. This is important for applications where the current
    * may get very small for long periods of time. Small currents can be
    * inaccurate by a high percentage, which leads to very inaccurate
    * accumulations.
    *
    * @param <code>byte</code> specifying whether 0,2,4 or 8 should be the
    * minimum number of bits a current measurement must have to be accumulated.
    * <p>Only 0,2,4 and 8 are valid parameters.
    *
    * @param thresholdValue
    *
    * @throws OneWireIOException Error setting the threshold [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void setThreshold (byte thresholdValue)
      throws OneWireIOException, OneWireException
   {
      byte   thresholdReg;
      byte[] data;

      switch (thresholdValue)
      {

         case 0 :
            thresholdReg = 0;
            break;
         case 2 :
            thresholdReg = 64;
            break;
         case 4 :
            thresholdReg = ( byte ) 128;
            break;
         case 8 :
            thresholdReg = ( byte ) 192;
            break;
         default :
            throw new IllegalArgumentException(
               "OneWireContainer26-Threshold value must be 0,2,4, or 8.");
      }

      // first save their original IAD settings so we dont change anything
      boolean IADvalue = getFlag(IAD_FLAG);

      // current measurements must be off to write to the threshold register
      setFlag(IAD_FLAG, false);

      // write the threshold register
      data     = readPage(0);
      data [7] = thresholdReg;

      writePage(0, data, 0);

      // set the IAD back to the way the user had it
      setFlag(IAD_FLAG, IADvalue);
   }

   /**
    * Retrieves the current ICA value in mVHr.
    *
    * @return <code>int</code> with the value in the ICA register.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public int getICA ()
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(1);

      return ( int ) (data [4] & 0x000000ff);
   }

   /**
    * Retrieves the current CCA value in mVHr. This value is accumulated over
    * the lifetime of the part (until it is set to 0 or the CA flag is set
    * to false) and includes only charging current (positive).
    *
    * @return <code>int</code> representation of the CCA value.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public int getCCA ()
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(7);

      return ((data [5] << 8) & 0x0000ff00) | (data [4] & 0x000000ff);
   }

   /**
    * Retrieves the value of the DCA in mVHr. This value is accumulated over
    * the lifetime of the part (until explicitly set to 0 or if the CA flag
    * is set to false) and includes only discharging current (negative).
    *
    * @return <code>int</code> representation of the DCA value.
    *
    * @throws OneWireIOException Error reading data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public int getDCA ()
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(7);

      return ((data [7] << 8) & 0x0000ff00) | (data [6] & 0x000000ff);
   }

   /**
    * Set the value of the ICA.
    *
    * @param <code>int</code> ranging from 0-255 to store in ICA.
    *
    * @param icaValue
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void setICA (int icaValue)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(1);

      data [4] = ( byte ) (icaValue & 0x000000ff);

      writePage(1, data, 0);
   }

   /**
    * Set the value of the CCA.
    *
    * @param <code>int</code> ranging from 0-255 to store in CCA.
    *
    * @param ccaValue
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void setCCA (int ccaValue)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(7);

      data [4] = ( byte ) (ccaValue & 0x00ff);
      data [5] = ( byte ) ((ccaValue & 0xff00) >>> 8);

      writePage(7, data, 0);
   }

   /**
    * Set the value of the DCA.
    *
    * @param <code>int</code> ranging from 0-255 to store in DCA.
    *
    * @param dcaValue
    *
    * @throws OneWireIOException Error writing data [ recoverable ]
    * @throws OneWireException Could not find part [ fatal ]
    * @throws IllegalArgumentException Bad parameters passed [recoverable ]
    */
   public void setDCA (int dcaValue)
      throws OneWireIOException, OneWireException, IllegalArgumentException
   {
      byte[] data = readPage(7);

      data [6] = ( byte ) (dcaValue & 0x00ff);
      data [7] = ( byte ) ((dcaValue & 0xff00) >>> 8);

      writePage(7, data, 0);
   }

   /**
    * This method extracts the Clock Value in milliseconds from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * occured since 1970.
    */
   public long getDisconnectTime (byte[] state)
   {
      return getTime(state, 16) * 1000;
   }

   /**
    * This method extracts the Clock Value in milliseconds from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * occured since 1970.
    */
   public long getEndOfChargeTime (byte[] state)
   {
      return getTime(state, 20) * 1000;
   }

   //actually could be called byteArrayToLong, only used in time functions
   private long getTime (byte[] state, int start)
   {
      long time = (state [start] & 0x0ff)
                  | ((state [start + 1] & 0x0ff) << 8)
                  | ((state [start + 2] & 0x0ff) << 16)
                  | ((state [start + 3] & 0x0ff) << 24);

      return time & 0x0ffffffff;
   }

   //////////////////////////////////////////////////////////////////////////////
   //
   //      INTERFACE METHODS!!!!!!!!
   //
   //////////////////////////////////////////////////////////////////////////////

   /**
     * Query to get the number of channels supported by this A/D.
     * Channel specific methods will use a channel number specified
     * by an integer from [0 to (getNumberChannels() - 1)].
     *
     * @return int containing the number of channels
     */
   public int getNumberADChannels ()
   {
      return 2;   //has VDD, VAD channel  (battery, gen purpose)
   }

   /**
    * Query to see if this A/D measuring device has high/low
    * alarms.
    *
    * @return boolean, true if has high/low trips
    */
   public boolean hasADAlarms ()
   {
      return false;
   }

   /**
    * Query to get an array of available ranges for the specified
    * A/D channel.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    *
    * @return array of double indicated the available ranges.
    */
   public double[] getADRanges (int channel)
   {
      double[] result = new double [1];

      result [0] = 10.23;

      /* for VAD, not entirely true--this should be
         2 * VDD.  If you hook up VDD to the
         one-wire in series with a diode and then
         hang a .1 microF capacitor off the line to ground,
         you can get about 9.5 for the high end accurately
                       ----------------------------------
                       |             *****************  |
         One-Wire------- DIODE-------*VDD     ONEWIRE*---
                                 |   *               *
                                 |   *        GROUND *---
                                 C   *               *  |
                                 |   *    2438       *  |
                                gnd  *               *  |
                                 |   *****************  |
                                 |----------------------|

       */
      return result;
   }

   /**
    * Query to get an array of available resolutions based
    * on the specified range on the specified A/D channel.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param range
    *
    * @return array of double, indicated the available resolutions
    */
   public double[] getADResolutions (int channel, double range)
   {
      double[] result = new double [1];

      result [0] = 0.01;   //10 mV

      return result;
   }

   /**
    * Query to see if this A/D supports doing multiple voltage
    * conversions at the same time.
    *
    * @return boolean, true if can do multi-channel voltage reads.
    */
   public boolean canADMultiChannelRead ()
   {
      return false;
   }

   //--------
   //-------- A/D IO Methods
   //--------

   /**
    * This method is used to perform voltage conversion on all specified
    * channels.  The method 'getVoltage()' can be used to read the result
    * of the conversion.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doADConvert (int channel, byte[] state)
      throws OneWireIOException, OneWireException
   {
      setFlag(AD_FLAG, channel == CHANNEL_VDD);

      // first perform the conversion
      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {
         adapter.putByte(CONVERT_VOLTAGE_COMMAND);

         try
         {
            Thread.sleep(4);
         }
         catch (InterruptedException e){}

         byte[] data = readPage(0);

         //let's update state with this info
         System.arraycopy(data, 0, state, 0, 8);

         // save off the voltage in our state's holdindg area
         state [24 + channel * 2]     = data [4];
         state [24 + channel * 2 + 1] = data [3];
      }
      else
         throw new OneWireException("OneWireContainer26-Device not found.");
   }

   /**
    * This method is used to perform voltage conversion on all specified
    * channels.  The method 'getVoltage()' can be used to read the result
    * of the conversion. This A/D must support multi-channel read
    * 'canMultiChannelRead()' if there are more then 1 channel is specified.
    *
    * @param doConvert - boolean array representing which channels
    *                    to perform conversion on.
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doADConvert (boolean[] doConvert, byte[] state)
      throws OneWireIOException, OneWireException
   {
      throw new OneWireException("This device cannot do multi-channel reads");
   }

   /**
    * This method is used to read the voltage values.  Must
    * be used after a 'doADConvert()' method call.  Also must
    * include the last valid state from the 'readDevice()' method
    * and this A/D must support multi-channel read 'canMultiChannelRead()'
    * if there are more then 1 channel.
    *
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double[] representing the voltage values for all channels
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public double[] getADVoltage (byte[] state)
      throws OneWireIOException, OneWireException
   {
      throw new OneWireException("This device cannot do multi-channel reads");
   }

   /**
    * This method is used to read a channels voltage value.  Must
    * be used after a 'doADConvert()' method call.  Also must
    * include the last valid state from the 'readDevice()' method.
    * Note, if more then one channel is to be read then it is more
    * efficient to use the 'getVoltage()' method that returns all
    * channel values.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double representing the voltage value for the specified
    *                  channel
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public double getADVoltage (int channel, byte[] state)
      throws OneWireIOException, OneWireException
   {
      double result =
         (((state [24 + channel * 2] << 8) & 0x0ff00) | (state [24 + channel * 2 + 1] & 0x0ff))
         / 100.0;

      return result;
   }

   //--------
   //-------- A/D 'get' Methods
   //--------

   /**
    * This method is used to extract the alarm voltage value of the
    * specified channel from the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    *
    * @return - double representing the alarm_value in volts
    */
   public double getADAlarm (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to extract the alarm enable value of the
    * specified channel from the provided state buffer.  The state
    * buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the state
    *               returned from 'readDevice()'.
    *
    * @return - boolean, true if specified alarm is enabled
    */
   public boolean getADAlarmEnable (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to check the alarm event value of the
    * specified channel from the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of the current state of the state
                    returned from 'readDevice()'.
    *
    * @return - boolean, true if specified alarm occurred
    */
   public boolean hasADAlarmed (int channel, int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to extract the conversion resolution of the
    * specified channel from the provided state buffer expressed in
    * volts.  The state is retrieved from the
    * 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the state
    *               returned from 'readDevice()'.
    *
    * @return - double, resolution of channel in volts
    */
   public double getADResolution (int channel, byte[] state)
   {

      //this is easy, its always 0.01 V = 10 mV
      return 0.01;
   }

   /**
    * This method is used to extract the input voltage range of the
    * specified channel from the provided state buffer.  The state
    * buffer is retrieved from the 'readDevice()' method.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param state - byte array of the current state of the state
    *                  returned from 'readDevice()'.
    *
    * @return - double representing the input voltage range
    */
   public double getADRange (int channel, byte[] state)
   {
      return 10.23;
   }

   //--------
   //-------- A/D 'set' Methods
   //--------

   /**
    * This method is used to set the alarm voltage value of the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarm - double, alarm value (will be reduced to 8 bit resolution).
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADAlarm (int channel, int alarmType, double alarm,
                           byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to set the alarm enable value of the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param alarmType - int, representing the desired alarm, ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarmEnable - boolean, alarm enable value
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADAlarmEnable (int channel, int alarmType,
                                 boolean alarmEnable, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have AD alarms");
   }

   /**
    * This method is used to set the conversion resolution value for the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param resolution - double, resolution to use in volts
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADResolution (int channel, double resolution, byte[] state)
   {

      //but you can't select the resolution for this part!!!!
      //just make it an airball
   }

   /**
    * This method is used to set the input range for the
    * specified channel in the provided state buffer.  The
    * state buffer is retrieved from the 'readDevice()' method.
    * The method 'writeDevice()' must be called to finalize these
    * changes to the device.  Note that multiple 'set' methods can
    * be called before one call to 'writeDevice()'.
    *
    * @param channel - integer specifying channel in the range
    *                  [0 to (getNumberChannels() - 1)].
    * @param range - double, representing the max volt range, use
    *                'getRanges()' method to get available ranges
    * @param state - byte array of the current state of the
    *               device returned from 'readDevice()'.
    */
   public void setADRange (int channel, double range, byte[] state)
   {

      //you can't change the ranges here without changing VDD!!!
      //just make this function call an airball
   }

   /**
    * This method retrieves the 1-Wire device sensor state.  This state is
    * returned as a byte array.  Pass this byte array to the static query
    * and set methods.  If the device state needs to be changed then call
    * the 'writeDevice' to finalize the one or more change.
    *
    * @return <code>byte[]<\code> 1-Wire device sensor state
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public byte[] readDevice ()
      throws OneWireIOException, OneWireException
   {

      //should return the first three pages
      //and then 4 extra bytes, 2 for channel 1 voltage and
      //2 for channel 2 voltage
      byte[] state = new byte [28];

      for (int i = 0; i < 3; i++)
      {
         byte[] pg = readPage(i);

         System.arraycopy(pg, 0, state, i * 8, 8);
      }

      //the last four bytes are used this way...
      //the current voltage reading is kept in page 0, 
      //but if a new voltage reading is asked for we move it to the
      //end so it can be there in case it is asked for again,
      //so we kind of weasel around this whole ADcontainer thing

      /* here's a little map
           byte[24] VDD high byte
           byte[25] VDD low byte
           byte[26] VAD high byte
           byte[27] VAD low byte
      */
      return state;
   }

   /**
    * This method write the 1-Wire device sensor state that
    * have been changed by the 'set' methods.  It knows which registers have
    * changed by looking at the bitmap fields appended to the state
    * data.
    *
    * @param  state - byte array of clock register page contents
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void writeDevice (byte[] state)
      throws OneWireIOException, OneWireException
   {
      writePage(0, state, 0);
      writePage(1, state, 8);
   }

   //--------
   //-------- Temperature Feature methods
   //--------

   /**
    * Query to see if this temperature measuring device has high/low
    * trip alarms.
    *
    * @return boolean, true if has high/low trip alarms
    */
   public boolean hasTemperatureAlarms ()
   {
      return false;
   }

   /**
    * Query to see if this device has selectable resolution.
    *
    * @return boolean, true if has selectable resolution
    */
   public boolean hasSelectableTemperatureResolution ()
   {
      return false;
   }

   /**
    * Query to get an array of available resolutions in degrees C.
    *
    * @return double[], available of resolutions in degrees C
    */
   public double[] getTemperatureResolutions ()
   {
      double[] result = new double [1];

      result [0] = 0.03125;

      return result;
   }

   /**
    * Query to get the high/low resolution in degrees C.
    *
    * @return double, high/low resolution resolution in C
    */
   public double getTemperatureAlarmResolution ()
      throws OneWireException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * Query to get the maximum temperature in degrees C.
    *
    * @return double, maximum temperature in C
    */
   public double getMaxTemperature ()
   {
      return 125.0;
   }

   /**
    * Query to get the minimum temperature in degrees C.
    *
    * @return double, minimum temperature in C
    */
   public double getMinTemperature ()
   {
      return -55.0;
   }

   //--------
   //-------- Temperature I/O Methods
   //--------

   /**
    * Perform an temperature conversion.  Use this state information
    * to calculate the conversion time.
    *
    * @param state - byte array of device state
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public void doTemperatureConvert (byte[] state)
      throws OneWireIOException, OneWireException
   {
      byte[] data;   // hold page

      doSpeed();
      adapter.reset();

      if (adapter.select(address))
      {

         // perform the temperature conversion
         adapter.putByte(CONVERT_TEMP_COMMAND);

         try
         {
            Thread.sleep(10);
         }
         catch (InterruptedException Ie){}

         data      = readPage(0);
         state [2] = data [2];
         state [1] = data [1];
      }
      else
         throw new OneWireException("OneWireContainer26-Device not found.");
   }

   //--------
   //-------- Temperature 'get' Methods
   //--------

   /**
    * This method extracts the Temperature Value in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return double, temperature in degrees C from the last 'doTemperature()'
    */
   public double getTemperature (byte[] state)
   {
      double temp = (( short ) ((state [2] << 8) | (state [1] & 0x0ff)) >> 3)
                    * 0.03125;

      return temp;
   }

   /**
    * This method extracts the specified Alarm value in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param alarmType - integer, indicating trip type ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param state - byte array of device state
    *
    * @return double, alarm trip temperature in degrees C
    */
   public double getTemperatureAlarm (int alarmType, byte[] state)
      throws OneWireException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * This method extracts the current resolution in degrees C from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return double, temperature resolution in degrees C
    */
   public double getTemperatureResolution (byte[] state)
   {
      return 0.03125;
   }

   //--------
   //-------- Temperature 'set' Methods
   //--------

   /**
    * This method sets the alarm value in degrees C in the
    * provided state data.  Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param alarmType - integer, indicating trip type ALARM_HIGH (1)
    *               or ALARM_LOW (0)
    * @param alarmValue - double, high trip value in degrees C
    * @param state - byte array of device state
    */
   public void setTemperatureAlarm (int alarmType, double alarmValue,
                                    byte[] state)
      throws OneWireException, OneWireIOException
   {
      throw new OneWireException(
         "This device does not have temperature alarms");
   }

   /**
    * This method sets the current resolution in degrees C in the
    * provided state data.   Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param resolution - double, temperature resolution in degrees C
    * @param state - byte array of device state
    */
   public void setTemperatureResolution (double resolution, byte[] state)
      throws OneWireException, OneWireIOException
   {

      //airball, only one resolution!!!
   }

   //--------
   //-------- Clock Feature methods
   //--------

   /**
    * Query to see if the clock has an alarm feature.
    *
    * @return boolean, true if real-time-clock has an alarm
    */
   public boolean hasClockAlarm ()
   {
      return false;
   }

   /**
    * Query to see if the clock can be disabled.  See
    * the methods 'isClockRunning()' and 'setClockRunEnable()'.
    *
    * @return boolean, true if the clock can be enabled and
    * disabled.
    */
   public boolean canDisableClock ()
   {
      return false;
   }

   /**
    * Query to get the clock resolution in milliseconds
    *
    * @return long, get the clock resolution in milliseconds.
    */
   public long getClockResolution ()
   {
      return 1000;
   }

   //--------
   //-------- Clock 'get' Methods
   //--------

   /**
    * This method extracts the Clock Value in milliseconds from the
    * state data retrieved from the 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * occured since 1970.
    */
   public long getClock (byte[] state)
   {
      return getTime(state, 8) * 1000;
   }

   /**
    * This method extracts the Clock Alarm Value from the provided
    * state data retrieved from the 'readDevice()'
    * method.
    *
    * @param state - byte array of device state
    *
    * @return <code>long<\code> time - in milliseconds that have
    * the clock alarm is set to.
    */
   public long getClockAlarm (byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have a clock alarm!");
   }

   /**
    * This method checks if the Clock Alarm flag has been set
    * from the state data retrieved from the
    * 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>boolean<\code> true if clock is alarming
    */
   public boolean isClockAlarming (byte[] state)
   {
      return false;
   }

   /**
    * This method checks if the Clock Alarm is enabled
    * from the provided state data retrieved from the
    * 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>boolean<\code> true if clock alarm is enabled
    */
   public boolean isClockAlarmEnabled (byte[] state)
   {
      return false;
   }

   /**
    * This method checks if the device's oscilator is enabled.  The clock
    * will not increment if the clock is not enabled.
    * This value is read from the provided state data retrieved from the
    * 'readDevice()' method.
    *
    * @param state - byte array of device state
    *
    * @return <code>boolean<\code> true
    */
   public boolean isClockRunning (byte[] state)
   {
      return true;
   }

   //--------
   //-------- Clock 'set' Methods
   //--------

   /**
    * This method sets the Clock time in the provided state data
    * Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param time - <code>long<\code> milliseconds the user
    * wants the Clock set to.
    * @param state - byte array of device state
    */
   public void setClock (long time, byte[] state)
   {
      time       = time / 1000;   //convert to seconds
      state [8]  = ( byte ) time;
      state [9]  = ( byte ) (time >> 8);
      state [10] = ( byte ) (time >> 16);
      state [11] = ( byte ) (time >> 24);
   }

   /**
    * This method sets the Clock Alarm in the provided state
    * data.  Use the method 'writeDevice()' with
    * this data to finalize the change to the device.
    *
    * @param time - <code>long<\code> milliseconds the user
    * wants the Clock alarm set to.
    * @param state - byte array of device state
    */
   public void setClockAlarm (long time, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have a clock alarm!");
   }

   /**
    * This method sets the oscillator enable to the specified
    * value. Use the method 'writeDevice()' with this
    * data to finalize the change to the device.
    *
    * @param runEnable - boolean, true if want the clock oscillator to
    *                 be enabled.
    * @param state - byte array of device state
    */
   public void setClockRunEnable (boolean runEnable, byte[] state)
      throws OneWireException
   {
      if (!runEnable)
         throw new OneWireException(
            "This device's clock cannot be disabled!");
   }

   /**
    * This method sets the Clock Alarm enable. Use the method
    * 'writeDevice()' with this data to finalize the
    * change to the device.
    *
    * @param  alarmEnable - boolean, true to enable the clock alarm
    * @param state - byte array of device state
    */
   public void setClockAlarmEnable (boolean alarmEnable, byte[] state)
      throws OneWireException
   {
      throw new OneWireException("This device does not have a clock alarm!");
   }
}
