
/*---------------------------------------------------------------------------
 * 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.Address;
import java.util.Enumeration;
import java.util.Vector;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.OneWireException;


/**
 * General iButton container class with basic communication functions.
 * This class should only be used if a device specific class is not available
 * or known.  Most OneWireContainer classes will extend this basic class.
 *
 *  @version    0.00, 28 Aug 2000
 *  @author     DS
 */
public class OneWireContainer
{

   //--------
   //-------- Variables 
   //--------

   /**
    * Reference to the adapter that is needed to communicate with this
    * iButton or 1-Wire device.
    */
   protected DSPortAdapter adapter;

   /**
    * 1-Wire Network Address number of this iButton or 1-Wire
    * device.
    * Family code is byte at offset 0.
    */
   protected byte[] address;

   /**
    * Temporary copy of 1-Wire Network Address number of this
    * iButton or 1-Wire device.
    */
   protected byte[] addressCopy;

   /**
    * Communication speed requested.
    * <ul>
    * <li>     0 (SPEED_REGULAR)
    * <li>     1 (SPEED_FLEX)
    * <li>     2 (SPEED_OVERDRIVE)
    * <li>     3 (SPEED_HYPERDRIVE)
    * <li>    >3 future speeds
    * </ul>
    */
   protected int speed;

   /**
    * Flag to indicate that falling back to a slower speed then requested
    * is OK.
    */
   protected boolean speedFallBackOK;

   //--------
   //-------- Constructors
   //--------

   /**
    * Constructor OneWireContainer
    *
    *
    */
   public OneWireContainer ()
   {
   }

   /**
    * 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 OneWireContainer (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      this.setupContainer(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 OneWireContainer (DSPortAdapter sourceAdapter, long newAddress)
   {
      this.setupContainer(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 OneWireContainer (DSPortAdapter sourceAdapter, String newAddress)
   {
      this.setupContainer(sourceAdapter, newAddress);
   }

   //--------
   //-------- Setup and adapter methods 
   //--------

   /**
    * Provide this container the adapter object used to access this device
    * and provide the address of this iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public void setupContainer (DSPortAdapter sourceAdapter, byte[] newAddress)
   {

      // get a reference to the source adapter (will need this to communicate)    
      adapter = sourceAdapter;

      // set the Address
      synchronized (this)
      {
         address     = new byte [8];
         addressCopy = new byte [8];

         System.arraycopy(newAddress, 0, address, 0, 8);
      }

      // set desired speed to be SPEED_REGULAR by default with no fallback
      speed           = adapter.SPEED_REGULAR;
      speedFallBackOK = false;
   }

   /**
    * Provide this container the adapter object used to access this device
    * and provide the address of this iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public void setupContainer (DSPortAdapter sourceAdapter, long newAddress)
   {

      // get a reference to the source adapter (will need this to communicate)    
      adapter = sourceAdapter;

      // set the Address
      synchronized (this)
      {
         address     = Address.toByteArray(newAddress);
         addressCopy = new byte [8];
      }

      // set desired speed to be SPEED_REGULAR by default with no fallback
      speed           = adapter.SPEED_REGULAR;
      speedFallBackOK = false;
   }

   /**
    * Provide this container the adapter object used to access this device
    * and provide the address of this iButton or 1-Wire device.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this iButton.
    * @param  newAddress        address of this 1-Wire device
    */
   public void setupContainer (DSPortAdapter sourceAdapter, String newAddress)
   {

      // get a reference to the source adapter (will need this to communicate)    
      adapter = sourceAdapter;

      // set the Address
      synchronized (this)
      {
         address     = Address.toByteArray(newAddress);
         addressCopy = new byte [8];
      }

      // set desired speed to be SPEED_REGULAR by default with no fallback
      speed           = adapter.SPEED_REGULAR;
      speedFallBackOK = false;
   }

   /**
    * Retrieve the port adapter object used to create this container.
    *
    * @return  DSPortAdapter for this container.
    */
   public DSPortAdapter getAdapter ()
   {
      return adapter;
   }

   //--------
   //-------- Device information methods
   //--------

   /**
    * Retrieve the Dallas Semiconductor part number of the iButton
    * as a string.  For example 'Crypto iButton' or 'DS1992'.
    *
    * @return  string represetation of the iButton name.
    */
   public String getName ()
   {
      synchronized (this)
      {
         return "Device type: "
                + (((address [0] & 0x0FF) < 16)
                   ? ("0" + Integer.toHexString(address [0] & 0x0FF))
                   : Integer.toHexString(address [0] & 0x0FF));
      }
   }

   /**
    * Retrieve the alternate Dallas Semiconductor part numbers or names.
    * A 'family' of 1-Wire Network devices may have more than one part number
    * depending on packaging.  There can also be nicknames such as
    * 'Crypto iButton'.
    *
    * @return  <code>String</code> representation of the alternate names.
    */
   public String getAlternateNames ()
   {
      return "";
   }

   /**
    * Retrieve a short description of the function of the iButton type.
    *
    * @return  <code>String</code> representation of the function description.
    */
   public String getDescription ()
   {
      return "No description available.";
   }

   /**
    * This method takes an int representing the speed of data
    * transfer on the 1-Wire Network. Note this may be slower then the
    * devices maximum speed.  This method can be used by an application
    * to restrict the communication rate due 1-Wire line conditions. <p>
    *
    * @param speed
    * <ul>
    * <li>     0 (SPEED_REGULAR) set to normal communciation speed
    * <li>     1 (SPEED_FLEX) set to flexible communciation speed used
    *            for long lines
    * <li>     2 (SPEED_OVERDRIVE) set to normal communciation speed to
    *            overdrive
    * <li>     3 (SPEED_HYPERDRIVE) set to normal communciation speed to
    *            hyperdrive
    * <li>    >3 future speeds
    * </ul>
    *
    * @param newSpeed
    *
    * @param fallBack boolean indicating it is OK to fall back to a slower
    *                 speed if true.
    */
   public void setSpeed (int newSpeed, boolean fallBack)
   {
      speed           = newSpeed;
      speedFallBackOK = fallBack;
   }

   /**
    * Returns the maximum speed this iButton or 1-Wire device can
    * communicate at.
    * Override this method if derived iButton type can go faster then
    * SPEED_REGULAR(0).
    */
   public int getMaxSpeed ()
   {
      return adapter.SPEED_REGULAR;
   }

   /**
    * Get the 1-Wire Network address of this device as an array of bytes.
    *
    * @return  array of bytes containing the iButton address
    */
   public byte[] getAddress ()
   {
      return address;
   }

   /**
    * Get this device's 1-Wire Network address as a String.
    *
    * @return <code>String</code> representation of the iButton address
    */
   public String getAddressAsString ()
   {
      return Address.toString(address);
   }

   /**
    * Get this device's 1-Wire Network address as a long.
    *
    * @return <code>String</code> representation of the iButton address
    */
   public long getAddressAsLong ()
   {
      return Address.toLong(address);
   }

   /**
    * Return an enumeration of memory banks.  Default is no memory banks.
    *
    * @return enumeration of memory banks to read and write memory
    *   on this iButton or 1-Wire device.
    */
   public Enumeration getMemoryBanks ()
   {
      return new Vector(0).elements();
   }

   //--------
   //-------- I/O Methods
   //--------

   /**
    * Verifies that the iButton or 1-Wire device specified is present on
    * the 1-Wire Network.
    *
    * @return  <code>true</code> if device present on the 1-Wire Network.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public boolean isPresent ()
      throws OneWireIOException, OneWireException
   {
      synchronized (this)
      {
         return adapter.isPresent(address);
      }
   }

   /**
    * Verifies that the iButton or 1-Wire device specified is present
    * on the 1-Wire Network and in an alarm state.  This does not
    * apply to all iButton types.
    *
    * @return  <code>true</code> if device present and in alarm condition.
    *
    * @throws OneWireIOException
    * @throws OneWireException
    */
   public boolean isAlarming ()
      throws OneWireIOException, OneWireException
   {
      synchronized (this)
      {
         return adapter.isAlarming(address);
      }
   }

   /**
    * Go to the specified speed for this container.  This method uses the
    * containers selected speed (method setSpeed(speed, fallback)) and
    * will optionally fall back to a slower speed if communciation failed.
    * Only call this method once to get the device into the desired speed
    * as long as the device is still responding.
    *
    * @throws OneWireIOException WHEN selected speed fails and fallback
    *                                 is false
    * @throws OneWireException WHEN hypterdrive is selected speed
    */
   public void doSpeed ()
      throws OneWireIOException, OneWireException
   {

      // check if already at speed and device present
      if ((speed == adapter.getSpeed()) && adapter.isPresent(address))
         return;

      // speed Overdrive
      if (speed == adapter.SPEED_OVERDRIVE)
      {

         // get this device and adapter to overdrive
         adapter.setSpeed(adapter.SPEED_REGULAR);
         adapter.reset();
         adapter.putByte(( byte ) 0x69);
         adapter.setSpeed(adapter.SPEED_OVERDRIVE);

         // get copy of address
         synchronized (this)
         {
            System.arraycopy(address, 0, addressCopy, 0, 8);
         }

         adapter.dataBlock(addressCopy, 0, 8);

         // check if new speed is OK
         if (!adapter.isPresent(address))
         {

            // check if allow fallback 
            if (speedFallBackOK)
               adapter.setSpeed(adapter.SPEED_REGULAR);
            else
               throw new OneWireIOException(
                  "Failed to get device to selected speed (overdrive)");
         }
      }

      // speed regular or flex
      else if ((speed == adapter.SPEED_REGULAR)
               || (speed == adapter.SPEED_FLEX))
         adapter.setSpeed(speed);

         // speed hyperdrive, don't know how to do this
      else
         throw new OneWireException(
            "Speed selected (hyperdrive) is not supported by this method");
   }
}
