// CHECKSTYLE:OFF

/*---------------------------------------------------------------------------
 * Copyright (C) 1999 - 2001 Maxim Integrated Products, 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 MAXIM INTEGRATED PRODUCTS 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 Maxim Integrated Products
 * shall not be used except as stated in the Maxim Integrated Products
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

package com.dalsemi.onewire.container;

import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import com.dalsemi.onewire.OneWireException;
import com.dalsemi.onewire.adapter.DSPortAdapter;
import com.dalsemi.onewire.adapter.OneWireIOException;
import com.dalsemi.onewire.utils.Convert;

/**
 * <P>
 * 1-Wire&reg; container for a Thermochron iButton, DS1921. This container
 * encapsulates the functionality of the 1-Wire family type <B>21</B> (hex).
 * </P>
 *
 * <H3>Features</H3>
 * <UL>
 * <LI>Logs up to 2048 consecutive temperature measurements in nonvolatile,
 * read-only memory
 * <li>Real-Time clock with programmable alarm
 * <LI>Programmable high and low temperature alarms
 * <li>Alarm violation times and durations recorded in nonvolatile, read-only
 * memory
 * <li>Automatically 'wakes up' and logs temperature at user-programmable
 * intervals
 * <li>4096 bits of general-purpose read/write nonvolatile memory
 * <li>256-bit scratchpad ensures integrity of data transfer
 * <li>On-chip 16-bit CRC generator to verify read operations
 * <li>Long-term histogram with 2@htmlonly &#176C @endhtmlonly resolution
 * </UL>
 *
 * <H3>Memory</H3>
 *
 * <P>
 * The memory can be accessed through the objects that are returned from the
 * {@link #getMemoryBanks() getMemoryBanks} method.
 * </P>
 *
 * The following is a list of the MemoryBank instances that are returned:
 *
 * <UL>
 * <LI><B> Scratchpad with CRC </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 32 starting at physical address 0
 * <LI><I> Features</I> Read/Write not-general-purpose volatile
 * <LI><I> Pages</I> 1 page of length 32 bytes
 * <LI><I> Page Features </I> page-device-CRC
 * <li><i> Extra information for each page</i> Target address, offset, length 3
 * </UL>
 * <LI><B> Main Memory </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 512 starting at physical address 0
 * <LI><I> Features</I> Read/Write general-purpose non-volatile
 * <LI><I> Pages</I> 16 pages of length 32 bytes giving 29 bytes Packet data
 * payload
 * <LI><I> Page Features </I> page-device-CRC
 * </UL>
 * <LI><B> Register control </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 32 starting at physical address 512
 * <LI><I> Features</I> Read/Write not-general-purpose non-volatile
 * <LI><I> Pages</I> 1 pages of length 32 bytes
 * <LI><I> Page Features </I> page-device-CRC
 * </UL>
 * <LI><B> Alarm time stamps </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 96 starting at physical address 544
 * <LI><I> Features</I> Read-only not-general-purpose non-volatile
 * <LI><I> Pages</I> 3 pages of length 32 bytes
 * <LI><I> Page Features </I> page-device-CRC
 * </UL>
 * <LI><B> Temperature histogram </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 128 starting at physical address 2048
 * <LI><I> Features</I> Read-only not-general-purpose non-volatile
 * <LI><I> Pages</I> 4 pages of length 32 bytes
 * <LI><I> Page Features </I> page-device-CRC
 * </UL>
 * <LI><B> Temperature log </B>
 * <UL>
 * <LI><I> Implements </I> {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank}
 * <LI><I> Size </I> 2048 starting at physical address 4096
 * <LI><I> Features</I> Read-only not-general-purpose non-volatile
 * <LI><I> Pages</I> 64 pages of length 32 bytes
 * <LI><I> Page Features </I> page-device-CRC
 * </UL>
 * </UL>
 *
 * <H3>Usage</H3>
 *
 * <p>
 * The code below starts a mission with the following characteristics:
 * <ul>
 * <li>Rollover flag enabled. This means if more than 2048 samples are taken,
 * the newer samples overwrite the oldest samples in the temperature log.</li>
 * <li>High alarm of 28.0@htmlonly &#176C @endhtmlonly and a low alarm of
 * 23.0@htmlonly &#176C @endhtmlonly. If the alarm is violated, the Temperature
 * Alarm log will record when and for how long the violation occurred.</li>
 * <li>The clock alarm enabled to Mondays at 12:30:45 pm.</li>
 * <li>Sets the Thermocron's Real-Time Clock to the host system's clock.</li>
 * <li>The mission will start in 2 minutes.</li>
 * <li>A sample rate of 1 minute.</li>
 * </ul>
 * This code also ensures that the Thermocron's clock is set to run, and that
 * the clock alarm is enabled.
 * </p>
 *
 * <pre>
 * <code>
 *       // "ID" is a byte array of size 8 with an address of a part we
 *       // have already found with family code 12 hex
 *       // "access" is a DSPortAdapter
 *       OneWireContainer21 ds1921 = (OneWireContainer21) access.getDeviceContainer(ID);
 *       ds1921.setupContainer(access,ID);
 *       //  ds1921 previously setup as a OneWireContainer21
 *       ds1921.clearMemory();
 *       //  read the current state of the device
 *       byte[] state = ds1921.readDevice();
 *       //  enable rollover
 *       ds1921.setFlag(ds1921.CONTROL_REGISTER, ds1921.ROLLOVER_ENABLE_FLAG, true, state);
 *       //  set the high temperature alarm to 28 C
 *       ds1921.setTemperatureAlarm(ds1921.ALARM_HIGH, 28.0, state);
 *       //  set the low temperature alarm to 23 C
 *       ds1921.setTemperatureAlarm(ds1921.ALARM_LOW, 23.0, state);
 *       //  set the clock alarm to occur weekly, Mondays at 12:30:45 pm
 *       ds1921.setClockAlarm(12, 30, 45, 2, ds1921.ONCE_PER_WEEK, state);
 *       //  set the real time clock to the system's current clock
 *       ds1921.setClock(System.currentTimeMillis(), state);
 *       //  set the mission to start in 2 minutes
 *       ds1921.setMissionStartDelay(2,state);
 *       //  make sure the clock is set to run
 *       ds1921.setClockRunEnable(true, state);
 *       //  make sure the clock alarm is enabled
 *       ds1921.setClockAlarmEnable(true, state);
 *       //  write all that information out
 *       ds1921.writeDevice(state);
 *       //  now enable the mission with a sample rate of 1 minute
 *       ds1921.enableMission(1);
 * </code>
 * </pre>
 *
 * <p>
 * The following code processes the temperature log:
 * </p>
 * <code><pre>
 *       byte[] state = ds1921.readDevice();
 *       byte[] log = ds1921.getTemperatureLog(state);
 *       Calendar time_stamp = ds1921.getMissionTimeStamp(state);
 *       long time = time_stamp.getTime().getTime() + ds1921.getFirstLogOffset(state);
 *       int sample_rate = ds1921.getSampleRate(state);
 *
 *       System.out.println("TEMPERATURE LOG");
 *
 *       for (int i=0;i &lt; log.length;i++)
 *       {
 *           System.out.println("- Temperature recorded at  : "+(new Date(time)));
 *           System.out.println("-                     was  : "+ds1921.decodeTemperature(log[i])+" C");
 *           time += sample_rate * 60 * 1000;
 *       }
 * </pre></code>
 *
 * <p>
 * The following code processes the alarm histories:
 * </p>
 * <code><pre>
 *       byte[] high_history = ds1921.getAlarmHistory(ds1921.TEMPERATURE_HIGH_ALARM);
 *       byte[] low_history = ds1921.getAlarmHistory(ds1921.TEMPERATURE_LOW_ALARM);
 *       int sample_rate = ds1921.getSampleRate(state);
 *       int start_offset, violation_count;
 *       System.out.println("ALARM HISTORY");
 *       if (low_history.length==0)
 *       {
 *           System.out.println("- No violations against the low temperature alarm.");
 *           System.out.println("-");
 *       }
 *       for (int i=0;i &lt; low_history.length/4; i++)
 *       {
 *           start_offset = (low_history [i * 4] & 0x0ff)
 *                     | ((low_history [i * 4 + 1] &lt;&lt; 8) & 0x0ff00)
 *                     | ((low_history [i * 4 + 2] &lt;&lt; 16) & 0x0ff0000);
 *           violation_count = 0x0ff & low_history[i*4+3];
 *           System.out.println("- Low alarm started at     : "+(start_offset * sample_rate));
 *           System.out.println("-                          : Lasted "+(violation_count * sample_rate)+" minutes");
 *       }
 *       if (high_history.length==0)
 *       {
 *           System.out.println("- No violations against the high temperature alarm.");
 *           System.out.println("-");
 *       }
 *       for (int i=0;i &lt; high_history.length/4; i++)
 *       {
 *           start_offset = (high_history [i * 4] & 0x0ff)
 *                     | ((high_history [i * 4 + 1] &lt;&lt; 8) & 0x0ff00)
 *                     | ((high_history [i * 4 + 2] &lt;&lt; 16) & 0x0ff0000);
 *           violation_count = 0x0ff & high_history[i*4+3];
 *           System.out.println("- High alarm started at    : "+(start_offset * sample_rate));
 *           System.out.println("-                          : Lasted "+(violation_count * sample_rate)+" minutes");
 *       }
 * </pre></code>
 *
 * <p>
 * The following code processes the temperature histogram:
 * </p>
 * <code><pre>
 *       double resolution = ds1921.getTemperatureResolution();
 *       double histBinWidth = ds1921.getHistogramBinWidth();
 *       double start = ds1921.getHistogramLowTemperature();
 *       System.out.println("TEMPERATURE HISTOGRAM");
 *       for (int i=0;i &lt; histogram.length;i++)
 *       {
 *          System.out.println("- Histogram entry          : "
 *                             + histogram [i] + " at temperature "
 *                             + start + " to "
 *                             + ( start + (histBinWidth - resolution)) + " C");
 *          start += histBinWidth;
 *       }
 * </pre></code>
 *
 * <p>
 * Also see the usage examples in the
 * {@link com.dalsemi.onewire.container.TemperatureContainer
 * TemperatureContainer} and {@link com.dalsemi.onewire.container.ClockContainer
 * ClockContainer} interfaces.
 * </p>
 *
 * For examples regarding memory operations,
 * <uL>
 * <li>See the usage example in
 * {@link com.dalsemi.onewire.container.OneWireContainer OneWireContainer} to
 * enumerate the MemoryBanks.
 * <li>See the usage examples in {@link com.dalsemi.onewire.container.MemoryBank
 * MemoryBank} and {@link com.dalsemi.onewire.container.PagedMemoryBank
 * PagedMemoryBank} for bank specific operations.
 * </uL>
 *
 * <H3>DataSheet</H3>
 * <DL>
 * <DD><A HREF=
 * "http://pdfserv.maxim-ic.com/arpdf/DS1921L-F5X.pdf">http://pdfserv.maxim-ic.com/arpdf/DS1921L-F5X.pdf</A>
 * </DL>
 *
 * Also visit <a href="http://www.ibutton.com/ibuttons/thermochron.html">
 * http://www.ibutton.com/ibuttons/thermochron.html</a> for links to more
 * sources on the DS1921 Thermocron.
 *
 * @see com.dalsemi.onewire.container.OneWireSensor
 * @see com.dalsemi.onewire.container.SwitchContainer
 * @see com.dalsemi.onewire.container.TemperatureContainer
 *
 * @version 0.00, 28 Aug 2000
 * @author COlmstea, KLA
 *
 */
public class OneWireContainer21 extends OneWireContainer implements TemperatureContainer, ClockContainer {
	private boolean doSpeedEnable = true;

	/*
	 * privates! Memory commands.
	 */
	private static final byte WRITE_SCRATCHPAD_COMMAND = (byte) 0x0F;
	private static final byte READ_SCRATCHPAD_COMMAND = (byte) 0xAA;
	private static final byte COPY_SCRATCHPAD_COMMAND = (byte) 0x55;
	private static final byte READ_MEMORY_CRC_COMMAND = (byte) 0xA5;
	private static final byte CLEAR_MEMORY_COMMAND = (byte) 0x3C;
	private static final byte CONVERT_TEMPERATURE_COMMAND = (byte) 0x44;

	// Scratchpad access memory bank
	private MemoryBankScratchCRC scratch;

	// Register control memory bank
	private MemoryBankNVCRC register;

	// Alarms memory bank
	private MemoryBankNVCRC alarm;

	// Histogram memory bank
	private MemoryBankNVCRC histogram;

	// Log memory bank
	private MemoryBankNVCRC log;

	// Buffer to hold the temperature log in
	private final byte[] read_log_buffer = new byte[64 * 32]; // 64 pages X 32 bytes per page

	// should we update the Real time clock?
	private boolean updatertc = false;

	// Maxim/Maxim Integrated Products Part number
	private String partNumber = "DS1921";

	// Temperature range low temperaturein degrees Celsius
	// calculated through 12-bit field of 1-Wire Net Address
	private double temperatureRangeLow = -40.0;
	private double temperatureRangeHigh = 85.0;

	// Temperature range width in degrees Celsius
	// calculated through 12-bit field of 1-Wire Net Address
	// private double temperatureRangeWidth = 125.0;

	// Temperature resolution in degrees Celsius
	// calculated through 12-bit field of 1-Wire Net Address
	private double temperatureResolution = 0.5;

	// The temperature range at which the device will operate.
	private double temperatureOperatingRangeLow = -40.0;
	private double temperatureOperatingRangeHigh = 85.0;

	// Is this 1-Wire device a DS1921HZ?
	private boolean isDS1921HZ = false;

	/////////////////////////////////////////////
	//
	// PUBLIC's
	//
	/////////////////////////////////////////////

	/**
	 * Address of the status register. Used with the <code>getFlag</code> and
	 * <code>setFlag</code> methods to set and check flags indicating the
	 * Thermochron's status.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final int STATUS_REGISTER = 0x214;

	/**
	 * Address of the control register. Used with the <code>getFlag</code> and
	 * <code>setFlag</code> methods to set and check flags indicating the
	 * Thermochron's status.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final int CONTROL_REGISTER = 0x20E;

	/**
	 * Alarm frequency setting for the <code>setClockAlarm()</code> method. If the
	 * DS1921 Thermocron alarm is enabled and is not alarming, it will alarm on the
	 * next Real-Time Clock second.
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 */
	public static final byte ONCE_PER_SECOND = (byte) 0x1F;

	/**
	 * Alarm frequency setting for the <code>setClockAlarm()</code> method. If the
	 * DS1921 Thermocron alarm is enabled and is not alarming, it will alarm the
	 * next time the Real-Time Clock's 'second' value is equal to the Alarm Clock's
	 * 'second' value.
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 */
	public static final byte ONCE_PER_MINUTE = (byte) 0x17;

	/**
	 * Alarm frequency setting for the <code>setClockAlarm()</code> method. If the
	 * DS1921 Thermocron alarm is enabled and is not alarming, it will alarm the
	 * next time the Real-Time Clock's 'second' and 'minute' values are equal to the
	 * Alarm Clock's 'second' and 'minute' values.
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 */
	public static final byte ONCE_PER_HOUR = (byte) 0x13;

	/**
	 * Alarm frequency setting for the <code>setClockAlarm()</code> method. If the
	 * DS1921 Thermocron alarm is enabled and is not alarming, it will alarm the
	 * next time the Real-Time Clock's 'second', 'minute', and 'hour' values are
	 * equal to the Alarm Clock's 'second', 'minute', and 'hour' values.
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 */
	public static final byte ONCE_PER_DAY = (byte) 0x11;

	/**
	 * Alarm frequency setting for the <code>setClockAlarm()</code> method. If the
	 * DS1921 Thermocron alarm is enabled and is not alarming, it will alarm the
	 * next time the Real-Time Clock's 'second', 'minute', 'hour', and 'day of week'
	 * values are equal to the Alarm Clock's 'second', 'minute', 'hour', and 'day of
	 * week' values
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 */
	public static final byte ONCE_PER_WEEK = (byte) 0x10;

	/**
	 * Low temperature alarm value for the methods <code>getAlarmStatus()</code>,
	 * <code>getAlarmHistory()</code>, and <code>setTemperatureAlarm()</code>.
	 *
	 * @see #getAlarmStatus(byte,byte[])
	 * @see #getAlarmHistory(byte)
	 * @see #setTemperatureAlarm(int,double,byte[])
	 */
	public static final byte TEMPERATURE_LOW_ALARM = 4;

	/**
	 * High temperature alarm value for the methods <code>getAlarmStatus()</code>,
	 * <code>getAlarmHistory()</code>, and <code>setTemperatureAlarm()</code>.
	 *
	 * @see #getAlarmStatus(byte,byte[])
	 * @see #getAlarmHistory(byte)
	 * @see #setTemperatureAlarm(int,double,byte[])
	 */
	public static final byte TEMPERATURE_HIGH_ALARM = 2;

	/**
	 * Clock alarm value for the methods <code>getAlarmStatus()</code> and
	 * <code>isClockAlarming()</code>.
	 *
	 * @see #getAlarmStatus(byte,byte[])
	 * @see #isClockAlarming(byte[])
	 */
	public static final byte TIMER_ALARM = 1;

	/**
	 * CONTROL REGISTER FLAG: When enabled, the device will respond to conditional
	 * search command if a timer alarm has occurred.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte TIMER_ALARM_SEARCH_FLAG = 1;

	/**
	 * CONTROL REGISTER FLAG: When enabled, the device will respond to conditional
	 * search command if the temperature has reached the high temperature threshold.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte TEMP_HIGH_SEARCH_FLAG = 2;

	/**
	 * CONTROL REGISTER FLAG: When enabled, the device will respond to conditional
	 * search command if the temperature has reached the low temperature threshold.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte TEMP_LOW_SEARCH_FLAG = 4;

	/**
	 * CONTROL REGISTER FLAG: When enabled, the device will begin overwriting the
	 * earlier temperature measurements when the temperature log memory becomes
	 * full.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte ROLLOVER_ENABLE_FLAG = 8;

	/**
	 * CONTROL REGISTER FLAG: When DISABLED, the mission will start as soon as the
	 * sample rate is written.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte MISSION_ENABLE_FLAG = 16;

	/**
	 * CONTROL REGISTER FLAG: Must be enabled to allow a clear memory function. Must
	 * be set immediately before the command is issued.
	 *
	 * @see #clearMemory()
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte MEMORY_CLEAR_ENABLE_FLAG = 64;

	/**
	 * CONTROL REGISTER FLAG: When DISABLED, the real time clock will start working.
	 * Must be disabled for normal operation.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 * @see #setFlag(int,byte,boolean)
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public static final byte OSCILLATOR_ENABLE_FLAG = (byte) 128;

	/**
	 * STATUS REGISTER FLAG: Will read back true when a clock alarm has occurred.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte TIMER_ALARM_FLAG = 1;

	/**
	 * STATUS REGISTER FLAG: Will read back true when the temperature during a
	 * mission reaches or exceeds the temperature high threshold.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte TEMPERATURE_HIGH_FLAG = 2;

	/**
	 * STATUS REGISTER FLAG: Will read back true when a temperature equal to or
	 * below the low temperature threshold was detected on a mission.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte TEMPERATURE_LOW_FLAG = 4;

	/**
	 * STATUS REGISTER FLAG: Will read back true when a mission temperature
	 * conversion is in progress
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte SAMPLE_IN_PROGRESS_FLAG = 16;

	/**
	 * STATUS REGISTER FLAG: Will read back true when a mission is in progress.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte MISSION_IN_PROGRESS_FLAG = 32;

	/**
	 * STATUS REGISTER FLAG: Will read back true if the memory has been cleared.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte MEMORY_CLEARED_FLAG = 64;

	/**
	 * STATUS REGISTER FLAG: Will read back true if a temperature conversion of any
	 * kind is in progress.
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #getFlag(int,byte)
	 */
	public static final byte TEMP_CORE_BUSY_FLAG = (byte) 128;

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS1921
	 * Thermocron iButton. Note that the method
	 * <code>setupContainer(DSPortAdapter,byte[])</code> must be called to set the
	 * correct <code>DSPortAdapter</code> device address.
	 *
	 * @see com.dalsemi.onewire.container.OneWireContainer#setupContainer(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      setupContainer(DSPortAdapter,byte[])
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      OneWireContainer21(DSPortAdapter,byte[])
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer21(DSPortAdapter,long)
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer21(DSPortAdapter,String)
	 */
	public OneWireContainer21() {
		// initialize the memory banks
		this.initMem();
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS1921
	 * Thermocron iButton.
	 *
	 * @param sourceAdapter adapter object required to communicate with this iButton
	 * @param newAddress    address of this DS1921
	 *
	 * @see #OneWireContainer21()
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer21(DSPortAdapter,long)
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer21(DSPortAdapter,String)
	 */
	public OneWireContainer21(DSPortAdapter sourceAdapter, byte[] newAddress) {
		super(sourceAdapter, newAddress);

		// initialize the memory banks
		this.initMem();
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS1921
	 * Thermocron iButton.
	 *
	 * @param sourceAdapter adapter object required to communicate with this iButton
	 * @param newAddress    address of this DS1921
	 *
	 * @see #OneWireContainer21()
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,byte[])
	 *      OneWireContainer21(DSPortAdapter,byte[])
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer21(DSPortAdapter,String)
	 */
	public OneWireContainer21(DSPortAdapter sourceAdapter, long newAddress) {
		super(sourceAdapter, newAddress);

		// initialize the memory banks
		this.initMem();
	}

	/**
	 * Creates a new <code>OneWireContainer</code> for communication with a DS1921
	 * Thermocron iButton.
	 *
	 * @param sourceAdapter adapter object required to communicate with this iButton
	 * @param newAddress    address of this DS1921
	 *
	 * @see #OneWireContainer21()
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,long)
	 *      OneWireContainer21(DSPortAdapter,long)
	 * @see #OneWireContainer21(com.dalsemi.onewire.adapter.DSPortAdapter,java.lang.String)
	 *      OneWireContainer21(DSPortAdapter,String)
	 */
	public OneWireContainer21(DSPortAdapter sourceAdapter, String newAddress) {
		super(sourceAdapter, newAddress);

		// initialize the memory banks
		this.initMem();
	}

	/**
	 * Provides this container with the adapter object used to access this device
	 * 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
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public void setupContainer(DSPortAdapter sourceAdapter, byte[] newAddress) {
		super.setupContainer(sourceAdapter, newAddress);
		this.setThermochronVariables();
	}

	/**
	 * Provides this container with the adapter object used to access this device
	 * 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
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public void setupContainer(DSPortAdapter sourceAdapter, long newAddress) {
		super.setupContainer(sourceAdapter, newAddress);
		this.setThermochronVariables();
	}

	/**
	 * Provides this container with the adapter object used to access this device
	 * 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
	 * @see com.dalsemi.onewire.utils.Address
	 */
	@Override
	public void setupContainer(DSPortAdapter sourceAdapter, java.lang.String newAddress) {
		super.setupContainer(sourceAdapter, newAddress);
		this.setThermochronVariables();
	}

	/**
	 * Gets an enumeration of memory bank instances that implement one or more of
	 * the following interfaces: {@link com.dalsemi.onewire.container.MemoryBank
	 * MemoryBank}, {@link com.dalsemi.onewire.container.PagedMemoryBank
	 * PagedMemoryBank}, and {@link com.dalsemi.onewire.container.OTPMemoryBank
	 * OTPMemoryBank}.
	 *
	 * @return <CODE>Enumeration</CODE> of memory banks
	 */
	@Override
	public Enumeration<MemoryBank> getMemoryBanks() {
		var bank_vector = new Vector<MemoryBank>(6);

		// scratchpad
		bank_vector.addElement(this.scratch);

		// NVRAM
		bank_vector.addElement(new MemoryBankNVCRC(this, this.scratch));

		// Register page
		bank_vector.addElement(this.register);

		// Alarm time stamps and duration
		bank_vector.addElement(this.alarm);

		// Histogram
		bank_vector.addElement(this.histogram);

		// Log
		bank_vector.addElement(this.log);

		return bank_vector.elements();
	}

	// --------
	// -------- Private
	// --------

	/**
	 * Construct the memory banks used for I/O.
	 */
	private void initMem() {

		// scratchpad
		this.scratch = new MemoryBankScratchCRC(this);

		// Register
		this.register = new MemoryBankNVCRC(this, this.scratch);
		this.register.numberPages = 1;
		this.register.size = 32;
		this.register.bankDescription = "Register control";
		this.register.startPhysicalAddress = 0x200;
		this.register.generalPurposeMemory = false;

		// Alarm registers
		this.alarm = new MemoryBankNVCRC(this, this.scratch);
		this.alarm.numberPages = 3;
		this.alarm.size = 96;
		this.alarm.bankDescription = "Alarm time stamps";
		this.alarm.startPhysicalAddress = 544;
		this.alarm.generalPurposeMemory = false;
		this.alarm.readOnly = true;
		this.alarm.readWrite = false;

		// Histogram
		this.histogram = new MemoryBankNVCRC(this, this.scratch);
		this.histogram.numberPages = 4;
		this.histogram.size = 128;
		this.histogram.bankDescription = "Temperature Histogram";
		this.histogram.startPhysicalAddress = 2048;
		this.histogram.generalPurposeMemory = false;
		this.histogram.readOnly = true;
		this.histogram.readWrite = false;

		// Log
		this.log = new MemoryBankNVCRC(this, this.scratch);
		this.log.numberPages = 64;
		this.log.size = 2048;
		this.log.bankDescription = "Temperature log";
		this.log.startPhysicalAddress = 4096;
		this.log.generalPurposeMemory = false;
		this.log.readOnly = true;
		this.log.readWrite = false;
	}

	/**
	 * Sets the following, calculated from the 12-bit code of the 1-Wire Net
	 * Address: (All temperatures set to degrees Celsius) 1) The part numbers:
	 * DS1921L-F50 = physical range -40 to +85, operating range -40 to +85.
	 * DS1921L-F51 = physical range -40 to +85, operating range -10 to +85.
	 * DS1921L-F52 = physical range -40 to +85, operating range -20 to +85.
	 * DS1921L-F53 = physical range -40 to +85, operating range -30 to +85.
	 *
	 * DS1921H = physical range 15 to 46, operating range -40 to +85 DS1921Z =
	 * physical range -5 to 26, operating range -40 to +85 2) Temperature Range low
	 * temperature. 3) Temperature Range width in degrees Celsius. 4) Temperature
	 * Resolution. 5) If a DS1921H or DS1921Z is detected.
	 *
	 */
	private void setThermochronVariables() {
		// Get Temperature Range code, which is the first 12 (MSB) bits of the
		// unique serial number (after the CRC).
		var address = this.getAddress(); // retrieve 1-Wire net address to look at range code.
		var rangeCode = (address[6] & 0x0FF) << 4 | (address[5] & 0x0FF) >> 4;

		switch (rangeCode) {
		case 0x34C:
			this.partNumber = "DS1921L-F51";
			this.temperatureRangeLow = -40;
			this.temperatureRangeHigh = 85;
			this.temperatureResolution = 0.5;
			this.temperatureOperatingRangeLow = -10;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = false;
			break;
		case 0x254:
			this.partNumber = "DS1921L-F52";
			this.temperatureRangeLow = -40;
			this.temperatureRangeHigh = 85;
			this.temperatureResolution = 0.5;
			this.temperatureOperatingRangeLow = -20;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = false;
			break;
		case 0x15C:
			this.partNumber = "DS1921L-F53";
			this.temperatureRangeLow = -40;
			this.temperatureRangeHigh = 85;
			this.temperatureResolution = 0.5;
			this.temperatureOperatingRangeLow = -30;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = false;
			break;
		case 0x4F2:
			this.partNumber = "DS1921H-F5";
			this.temperatureRangeLow = 15;
			this.temperatureRangeHigh = 46;
			this.temperatureResolution = 0.125;
			this.temperatureOperatingRangeLow = -40;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = true;
			break;
		case 0x3B2:
			this.partNumber = "DS1921Z-F5";
			this.temperatureRangeLow = -5;
			this.temperatureRangeHigh = 26;
			this.temperatureResolution = 0.125;
			this.temperatureOperatingRangeLow = -40;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = true;
			break;
		default:
			var lower36bits = ((long) address[5] & 0x0F) << 32 | ((long) address[4] & 0x0FF) << 24
					| ((long) address[3] & 0x0FF) << 16 | ((long) address[2] & 0x0FF) << 8 | (long) address[1] & 0x0FF;
			if (lower36bits >= 0x100000) {
				this.partNumber = "DS1921G-F5";
			} else {
				this.partNumber = "DS1921L-PROTO";
			}

			this.temperatureRangeLow = -40;
			this.temperatureRangeHigh = 85;
			this.temperatureResolution = 0.5;
			this.temperatureOperatingRangeLow = -40;
			this.temperatureOperatingRangeHigh = 85;
			this.isDS1921HZ = false;
			break;

		}
		/*
		 * // Get Temperature Range code, which is the first 12 (MSB) bits of the //
		 * unique serial number (after the CRC). byte[] netAddress = getAddress(); //
		 * retrieve 1-Wire net address to look at range code. int rangeCode =
		 * (netAddress[6] & 0xFF); // And with 0xFF to get rid of sign extension
		 * rangeCode = rangeCode << 8; // left shift 8 bits to put most significant byte
		 * in correct place rangeCode = rangeCode + (netAddress[5] & 0xFF); // add the
		 * least significant byte to make integer. rangeCode = rangeCode >> 4; // this
		 * is a 12-bit number, so get rid of extra 4 bits.
		 *
		 * // Detect what kind of part we have, a DS1921L-F5X or a DS1921H/Z int
		 * detectionInt = rangeCode & 0x03; // get the last 2 bits to see what they are
		 * if (detectionInt > 0) isDS1921HZ = true; // if the last 2 bits > 0 then the
		 * part is a DS1921H or Z
		 *
		 * // Get temperature ranges as a result of the rangeCode and the type of
		 * device. if (isDS1921HZ) { // get the most significant 8 bits of the 12-bit
		 * rangeCode temperatureRangeLow = rangeCode >> 4; temperatureRangeLow =
		 * temperatureRangeLow - 64; // 1 degree increment with 0x000 = -64 degrees.
		 *
		 * // Resolution Code -- the last 2 bits of the 12-bit rangeCode number // // 0
		 * = 0.5 degrees Celsius // 1 = 0.25 // 2 = 0.125 // 3 = 0.0625 switch(rangeCode
		 * & 0x03) // gets the last 2 bits of the 12-bit rangeCode. { case 0:
		 * temperatureResolution = 0.5; break; case 1: temperatureResolution = 0.25;
		 * break; case 2: temperatureResolution = 0.125; break; case 3:
		 * temperatureResolution = 0.0625; break; default: temperatureResolution = 0.5;
		 * }
		 *
		 * // Range Modifier Code (range width) // // 0 = full range, 256 * resolution
		 * // 1 = reduced range, 2/3 of full range // 2 = reduced range, 1/2 of full
		 * range // 3 = reduced range, 1/3 of full range
		 *
		 * switch((rangeCode >> 2) & 0x03) { case 0: temperatureRangeWidth = (256 *
		 * temperatureResolution) - 1; break; case 1: temperatureRangeWidth = (256 *
		 * temperatureResolution * 2 / 3) - 1; break; case 2: temperatureRangeWidth =
		 * (256 * temperatureResolution / 2) - 1; break; case 3: temperatureRangeWidth =
		 * (256 * temperatureResolution / 3) - 1; break; default: temperatureRangeWidth
		 * = (256 * temperatureResolution) - 1; } } else { // get the most significant 5
		 * bits of the 12-bit rangeCode number. temperatureRangeLow = rangeCode >> 7;
		 * temperatureRangeLow = (temperatureRangeLow * 5) - 40; // 5 degree increment
		 * temperatureResolution = 0.5; // for non DS1921H/Z parts, the resolution is
		 * the same. // if part has a range code, get the next 5 bits of the 12-bit
		 * number. if (rangeCode > 0) temperatureRangeWidth = ((rangeCode >> 2) &
		 * (0x1F)) * 5; // 5 degree increment } // set the part number based (currently)
		 * on low temperature. switch((int) temperatureRangeLow) // switches on the low
		 * temperature. { case 15: partNumber = "DS1921H-F5"; break; case -5: partNumber
		 * = "DS1921Z-F5"; break; case -10: partNumber = "DS1921L-F51"; break; case -20:
		 * partNumber = "DS1921L-F52"; break; case -30: partNumber = "DS1921L-F53";
		 * break; case -40: partNumber = "DS1921L-F50"; break; default: partNumber =
		 * "DS1921"; }
		 */
	}

	/**
	 * Grab the date from one of the time registers. returns int[] = {year, month,
	 * date}
	 */
	private int[] getDate(int timeReg, byte[] state) {
		byte upper, lower;
		var result = new int[3];

		timeReg = timeReg & 31;

		/* extract the day of the month */
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x0f);
		lower = (byte) (lower & 0x0f);
		result[2] = 10 * upper + lower;

		/* extract the month */
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x0f);
		lower = (byte) (lower & 0x0f);

		// the upper bit contains the century, so subdivide upper
		var century = (byte) (upper >>> 3 & 0x01);

		upper = (byte) (upper & 0x01);
		result[1] = lower + upper * 10;

		/* grab the year */
		result[0] = 1900 + century * 100;
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x0f);
		lower = (byte) (lower & 0x0f);
		result[0] += upper * 10 + lower;

		return result;
	}

	/**
	 * Gets the time of day fields in 24-hour time from button returns int[] =
	 * {seconds, minutes, hours}
	 */
	private int[] getTime(int timeReg, byte[] state) {
		byte upper, lower;
		var result = new int[3];

		timeReg = timeReg & 31;

		// NOTE: The MSbit is ANDed out (with the 0x07) because alarm clock
		// registers have an extra bit to indicate alarm frequency

		/* First grab the seconds. Upper half holds the 10's of seconds */
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x07);
		lower = (byte) (lower & 0x0f);
		result[0] = lower + upper * 10;

		/* now grab minutes. The upper half holds the 10s of minutes */
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x07);
		lower = (byte) (lower & 0x0f);
		result[1] = lower + upper * 10;

		/*
		 * now grab the hours. The lower half is single hours again, but the upper half
		 * of the byte is determined by the 2nd bit - specifying 12/24 hour time.
		 */
		lower = state[timeReg++];
		upper = (byte) (lower >>> 4 & 0x07);
		lower = (byte) (lower & 0x0f);

		int hours;

		// if the 2nd bit is 1, convert 12 hour time to 24 hour time.
		if (upper >>> 2 != 0) {

			// extract the AM/PM byte (PM is indicated by a 1)
			var PM = (byte) (upper << 6 >>> 7 & 0x01);

			// isolate the 10s place
			upper = (byte) (upper & 0x01);
			hours = upper * 10 + PM * 12;
		} else {
			hours = upper * 10; // already in 24 hour format
		}

		hours += lower;
		result[2] = hours;

		return result;
	}

	/**
	 * Set the time in the DS1921 time register format.
	 */
	private void setTime(int timeReg, int hours, int minutes, int seconds, boolean AMPM, byte[] state) {
		byte upper, lower;

		/* format in bytes and write seconds */
		upper = (byte) (seconds / 10);
		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (seconds % 10);
		lower = (byte) (lower & 0x0f);
		state[timeReg & 31] = (byte) (upper | lower);

		timeReg++;

		/* format in bytes and write minutes */
		upper = (byte) (minutes / 10);
		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (minutes % 10);
		lower = (byte) (lower & 0x0f);
		state[timeReg & 31] = (byte) (upper | lower);

		timeReg++;

		/* format in bytes and write hours/(12/24) bit */
		if (AMPM) {
			upper = (byte) 0x04;

			if (hours > 11) {
				upper = (byte) (upper | 0x02);
			}

			// this next function simply checks for a decade hour
			if (hours % 12 == 0 || hours % 12 > 9) {
				upper = (byte) (upper | 0x01);
			}

			if (hours > 12) {
				hours = hours - 12;
			}

			if (hours == 0) {
				lower = (byte) 0x02;
			} else {
				lower = (byte) (hours % 10 & 0x0f);
			}
		} else {
			upper = (byte) (hours / 10);
			lower = (byte) (hours % 10);
		}

		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (lower & 0x0f);
		state[timeReg & 31] = (byte) (upper | lower);

		timeReg++;
	}

	/**
	 * Set the current date in the DS1921's real time clock.
	 *
	 * year - The year to set to, i.e. 2001. month - The month to set to, i.e. 1 for
	 * January, 12 for December. day - The day of month to set to, i.e. 1 to 31 in
	 * January, 1 to 30 in April.
	 */
	private void setDate(int year, int month, int day, byte[] state) {
		byte upper, lower;

		/* write the day byte (the upper holds 10s of days, lower holds single days) */
		upper = (byte) (day / 10);
		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (day % 10);
		lower = (byte) (lower & 0x0f);
		state[0x04] = (byte) (upper | lower);

		/*
		 * write the month bit in the same manner, with the MSBit indicating the century
		 * (1 for 2000, 0 for 1900)
		 */
		upper = (byte) (month / 10);
		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (month % 10);
		lower = (byte) (lower & 0x0f);

		if (year > 1999) {
			upper = (byte) (upper | 128);

			// go ahead and fix up the year too while i'm at it
			year = year - 2000;
		} else {
			year = year - 1900;
		}

		state[0x05] = (byte) (upper | lower);

		// now write the year
		upper = (byte) (year / 10);
		upper = (byte) (upper << 4 & 0xf0);
		lower = (byte) (year % 10);
		lower = (byte) (lower & 0x0f);
		state[0x06] = (byte) (upper | lower);
	}

	//////////////////////////////////////////////////////////////
	//
	// Public methods
	//
	//////////////////////////////////////////////////////////////

	/**
	 * Returns the maximum speed this iButton device can communicate at.
	 *
	 * @return maximum speed
	 * @see DSPortAdapter#setSpeed
	 */
	@Override
	public int getMaxSpeed() {
		return DSPortAdapter.SPEED_OVERDRIVE;
	}

	/**
	 * Gets the Maxim Integrated Products part number of the iButton or 1-Wire
	 * Device as a <code>java.lang.String</code>. For example "DS1992".
	 *
	 * @return iButton or 1-Wire device name
	 */
	@Override
	public String getName() {
		return this.partNumber;
	}

	/**
	 * Retrieves the alternate Maxim Integrated Products part numbers or names. A
	 * 'family' of MicroLAN devices may have more than one part number depending on
	 * packaging. There can also be nicknames such as "Crypto iButton".
	 *
	 * @return the alternate names for this iButton or 1-Wire device
	 */
	@Override
	public String getAlternateNames() {
		return "Thermochron";
	}

	/**
	 * Gets a short description of the function of this iButton or 1-Wire Device
	 * type.
	 *
	 * @return device description
	 */
	@Override
	public String getDescription() {
		// put the DS1921's characteristics together in a string format.
		var characteristics = "";
		if (this.partNumber != "DS1921") {
			// get the physical range as a string
			var strPhysicalRange = Convert.toString(this.getPhysicalRangeLowTemperature(), 1) + " to "
					+ Convert.toString(this.getPhysicalRangeHighTemperature(), 1) + " degrees Celsius.";
			// get the operating range as a string
			var strOperatingRange = Convert.toString(this.getOperatingRangeLowTemperature(), 1) + " to "
					+ Convert.toString(this.getOperatingRangeHighTemperature(), 1) + " degrees Celsius.";
			characteristics = " The operating range for this device is:  " + strOperatingRange
					+ " The physical range for this device is:  " + strPhysicalRange + " The resolution is "
					+ Convert.toString(this.getTemperatureResolution(), 3)
					+ " degrees Celsius, and the histogram bin width is "
					+ Convert.toString(this.getHistogramBinWidth(), 3) + " degrees Celsius.";
		}
		return "Rugged, self-sufficient 1-Wire device that, once setup for "
				+ "a mission, will measure the temperature and record the result in "
				+ "a protected memory section. It stores up to 2048 temperature "
				+ "measurements and will take measurements at a user-specified "
				+ "rate. The thermochron also records the number of times the temperature "
				+ "falls on a given degree range (temperature bin), and stores the " + "data in histogram format."
				+ characteristics;
	}

	/**
	 * Directs the container to avoid the calls to doSpeed() in methods that
	 * communicate with the Thermocron. To ensure that all parts can talk to the
	 * 1-Wire bus at their desired speed, each method contains a call to
	 * <code>doSpeed()</code>. However, this is an expensive operation. If a user
	 * manages the bus speed in an application, call this method with
	 * <code>doSpeedCheck</code> as <code>false</code>. The default behavior is to
	 * call <code>doSpeed()</code>.
	 *
	 * @param doSpeedCheck <code>true</code> for <code>doSpeed()</code> to be called
	 *                     before every 1-Wire bus access, <code>false</code> to
	 *                     skip this expensive call
	 *
	 * @see OneWireContainer#doSpeed()
	 */
	public synchronized void setSpeedCheck(boolean doSpeedCheck) {
		this.doSpeedEnable = doSpeedCheck;
	}

	/**
	 * This method returns the low temperature of the thermochron's physical
	 * temperature range. The physical range is the range of temperatures that the
	 * thermochron can record.
	 *
	 * The following is a list of physical ranges in degrees Celsius:
	 *
	 * DS1921L-F5X = physical range -40 to +85
	 *
	 * DS1921H = physical range 15 to 46
	 *
	 * DS1921Z = physical range -5 to 26
	 *
	 * @return the physical range low temperature in degrees Celsius
	 */
	public double getPhysicalRangeLowTemperature() {
		return this.temperatureRangeLow;
	}

	/**
	 * This method returns the high temperature of the thermochron's physical
	 * temperature range. The physical range is the range of temperatures that the
	 * thermochron can record.
	 *
	 * The following is a list of physical ranges in degrees Celsius:
	 *
	 * DS1921L-F5X = physical range -40 to +85
	 *
	 * DS1921H = physical range 15 to 46
	 *
	 * DS1921Z = physical range -5 to 26
	 *
	 * @return the physical range low temperature in degrees Celsius
	 */
	public double getPhysicalRangeHighTemperature() {
		return this.temperatureRangeHigh;
	}

	/**
	 * This method returns the low temperature of the thermochron's operating
	 * temperature range. The operating range is the range of temperatures for which
	 * the thermochron can function properly.
	 *
	 * The following is a list of operating ranges in degrees Celsius:
	 *
	 * DS1921L-F50 = operating range -40 to +85. DS1921L-F51 = operating range -10
	 * to +85. DS1921L-F52 = operating range -20 to +85. DS1921L-F53 = operating
	 * range -30 to +85.
	 *
	 * DS1921H = operating range -40 to +85 DS1921Z = operating range -40 to +85
	 *
	 * @return the operating range low temperature in degrees Celsius
	 */
	public double getOperatingRangeLowTemperature() {
		return this.temperatureOperatingRangeLow;
	}

	/**
	 * This method returns the high temperature of the thermochron's operating
	 * temperature range. The operating range is the range of temperatures for which
	 * the thermochron can function properly.
	 *
	 * The following is a list of operating ranges in degrees Celsius:
	 *
	 * DS1921L-F50 = operating range -40 to +85. DS1921L-F51 = operating range -10
	 * to +85. DS1921L-F52 = operating range -20 to +85. DS1921L-F53 = operating
	 * range -30 to +85.
	 *
	 * DS1921H = operating range -40 to +85 DS1921Z = operating range -40 to +85
	 *
	 * @return the operating range high temperature in degrees Celsius
	 */
	public double getOperatingRangeHighTemperature() {
		return this.temperatureOperatingRangeHigh;
	}

	/**
	 * Retrieves the resolution with which the thermochron takes temperatures in
	 * degrees Celsius.
	 *
	 * @return the temperature resolution of this thermochron.
	 */
	public double getTemperatureResolution() {
		return this.temperatureResolution;
	}

	/**
	 * Retrieves the lowest temperature of the first histogram bin in degrees
	 * Celsius.
	 *
	 * @return the lowest histogram bin temperature.
	 */
	public double getHistogramLowTemperature() {
		var lowTemp = this.getPhysicalRangeLowTemperature(); // low temp of thermochrons other than H or Z
		if (this.isDS1921HZ) {
			lowTemp = lowTemp - this.getTemperatureResolution() * 4;
		}
		return lowTemp;
	}

	/**
	 * This method returns the width of a histogram bin in degrees Celsius.
	 *
	 * @return the width of a histogram bin for this thermochron.
	 */
	public double getHistogramBinWidth() {
		return this.getTemperatureResolution() * 4; // 4 temperature readings per bin
	}

	/**
	 * Converts a temperature from the DS1921 <code>byte</code> encoded format to
	 * degrees Celsius. The raw temperature readings are unsigned <code>byte</code>
	 * values, representing a 2.0 degree accuracy.
	 *
	 * @param tempByte raw DS1921 temperature reading
	 *
	 * @return temperature in degrees Celsius
	 *
	 * @see #encodeTemperature(double)
	 */
	public double decodeTemperature(byte tempByte) {
		// the formula for DS1921H/Z:
		// C = Tbyte * Tres + (Tlow - (4 * Tres))
		// where C is decimal degrees Celsius.
		// and Tbyte is the byte to be decoded.
		// and Tlow is the low temperature of temperature range.
		// and Tres is the resolution of the DS1921.

		var decodedTemperature = 0.0;
		if (this.isDS1921HZ) {
			decodedTemperature = (tempByte & 0x00ff) * this.temperatureResolution;
			decodedTemperature = decodedTemperature + (this.temperatureRangeLow - 4 * this.temperatureResolution);
		} else {
			decodedTemperature = (tempByte & 0x00ff) / 2.0 - 40.0;
		}
		return decodedTemperature;
	}

	/**
	 * Converts a temperature in degrees Celsius to a <code>byte</code> formatted
	 * for the DS1921.
	 *
	 * @param temperature the temperature (Celsius) to convert
	 *
	 * @return the temperature in raw DS1921 format
	 *
	 * @see #decodeTemperature(byte)
	 */
	public byte encodeTemperature(double temperature) {
		// the formula for DS1921H/Z:
		// Tbyte = ((C - Tlow) / Tres) + 4;
		// where Tbyte is the byte to be encoded.
		// and C is decimal degrees Celsius
		// and Tlow is the low temperature of temperature range
		// and Tres is the resolution of the DS1921

		byte encodedTemperature = 0x00;
		if (this.isDS1921HZ) {
			var result = (temperature - this.temperatureRangeLow) / this.temperatureResolution + 4;
			encodedTemperature = (byte) ((int) result & 0x000000ff);
		} else {
			encodedTemperature = (byte) ((int) (2 * temperature) + 80 & 0x000000ff);
		}
		return encodedTemperature;
	}

	/**
	 * Writes a byte of data into the DS1921's memory. Note that writing to the
	 * register page while a mission is in progress ends that mission. Also note
	 * that the preferred way to write a page is through the <code>MemoryBank</code>
	 * objects returned from the <code>getMemoryBanks()</code> method.
	 *
	 * @param memAddr the address for writing (in the range of 0x200-0x21F)
	 * @param source  the data <code>byte</code> to write
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #readByte(int)
	 * @see #getMemoryBanks()
	 */
	public void writeByte(int memAddr, byte source) throws OneWireIOException, OneWireException {

		// User should only need to write to the 32 byte register page
		var buffer = new byte[5];

		// break the address into its bytes
		var msbAddress = (byte) (memAddr >>> 8 & 0x0ff);
		var lsbAddress = (byte) (memAddr & 0x0ff);

		/* check for valid parameters */
		if (msbAddress > 0x1F || msbAddress < 0) {
			throw new IllegalArgumentException("OneWireContainer21-Address for write out of range.");
		}

		/* perform the write and verification */
		if (this.doSpeedEnable) {
			this.doSpeed();
		}

		if (!this.adapter.select(this.address)) {
			throw new OneWireException("OneWireContainer21-Device not present.");
		}
		/* write to the scratchpad first */
		buffer[0] = WRITE_SCRATCHPAD_COMMAND;
		buffer[1] = lsbAddress;
		buffer[2] = msbAddress;
		buffer[3] = source;

		this.adapter.dataBlock(buffer, 0, 4);

		/* read it back for the verification bytes required to copy it to mem */
		this.adapter.select(this.address);

		buffer[0] = READ_SCRATCHPAD_COMMAND;

		for (var i = 1; i < 5; i++) {
			buffer[i] = (byte) 0x0ff;
		}

		this.adapter.dataBlock(buffer, 0, 5);

		// check to see if the data was written correctly
		if (buffer[4] != source) {
			throw new OneWireIOException("OneWireContainer21-Error writing data byte.");
		}

		/* now perform the copy from the scratchpad to memory */
		this.adapter.select(this.address);

		buffer[0] = COPY_SCRATCHPAD_COMMAND;

		// keep buffer[1]-buffer[3] because they contain the verification bytes
		buffer[4] = (byte) 0xff;

		this.adapter.dataBlock(buffer, 0, 5);

		/* now check to see that the part sent a 01010101 indicating a success */
		if (buffer[4] != (byte) 0xAA && buffer[4] != (byte) 0x55) {
			throw new OneWireIOException("OneWireContainer21-Error writing data byte.");
		}
	}

	/**
	 * Reads a single byte from the DS1921. Note that the preferred manner of
	 * reading from the DS1921 Thermocron is through the <code>readDevice()</code>
	 * method or through the <code>MemoryBank</code> objects returned in the
	 * <code>getMemoryBanks()</code> method.
	 *
	 * @param memAddr the address to read from (in the range of 0x200-0x21F)
	 *
	 * @return the data byte read
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #writeByte(int,byte)
	 * @see #readDevice()
	 * @see #getMemoryBanks()
	 */
	public byte readByte(int memAddr) throws OneWireIOException, OneWireException {
		var buffer = new byte[4];

		// break the address up into bytes
		var msbAddress = (byte) (memAddr >> 8 & 0x000000ff);
		var lsbAddress = (byte) (memAddr & 0x000000ff);

		/* check the validity of the address */
		if (msbAddress > 0x1F || msbAddress < 0) {
			throw new IllegalArgumentException("OneWireContainer21-Address for read out of range.");
		}

		/* read a user specified amount of memory and verify its validity */
		if (this.doSpeedEnable) {
			this.doSpeed();
		}

		if (this.adapter.select(this.address)) {
			buffer[0] = READ_MEMORY_CRC_COMMAND;
			buffer[1] = lsbAddress;
			buffer[2] = msbAddress;
			buffer[3] = (byte) 0x0ff;

			this.adapter.dataBlock(buffer, 0, 4);

			return buffer[3];
		}
		throw new OneWireException("OneWireContainer21-Device not present.");
	}

	/**
	 * <p>
	 * Gets the status of the specified flag from the specified register. This
	 * method actually communicates with the Thermocron. To improve performance if
	 * you intend to make multiple calls to this method, first call
	 * <code>readDevice()</code> and use the <code>getFlag(int, byte, byte[])</code>
	 * method instead.
	 * </p>
	 *
	 * <p>
	 * The DS1921 Thermocron has two sets of flags. One set belongs to the control
	 * register. When reading from the control register, valid values for
	 * <code>bitMask</code> are:
	 * </p>
	 * <ul>
	 * <li><code> TIMER_ALARM_SEARCH_FLAG  </code></li>
	 * <li><code> TEMP_HIGH_SEARCH_FLAG    </code></li>
	 * <li><code> TEMP_LOW_SEARCH_FLAG     </code></li>
	 * <li><code> ROLLOVER_ENABLE_FLAG     </code></li>
	 * <li><code> MISSION_ENABLE_FLAG      </code></li>
	 * <li><code> MEMORY_CLEAR_ENABLE_FLAG </code></li>
	 * <li><code> OSCILLATOR_ENABLE_FLAG   </code></li>
	 * </ul>
	 * <p>
	 * When reading from the status register, valid values for <code>bitMask</code>
	 * are:
	 * </p>
	 * <ul>
	 * <li><code> TIMER_ALARM_FLAG         </code></li>
	 * <li><code> TEMPERATURE_HIGH_FLAG    </code></li>
	 * <li><code> TEMPERATURE_LOW_FLAG     </code></li>
	 * <li><code> SAMPLE_IN_PROGRESS_FLAG  </code></li>
	 * <li><code> MISSION_IN_PROGRESS_FLAG </code></li>
	 * <li><code> MEMORY_CLEARED_FLAG      </code></li>
	 * <li><code> TEMP_CORE_BUSY_FLAG      </code></li>
	 * </ul>
	 *
	 * @param register address of register containing the flag (valid values are
	 *                 <code>CONTROL_REGISTER</code> and
	 *                 <code>STATUS_REGISTER</code>)
	 * @param bitMask  the flag to read (see above for available options)
	 *
	 * @return the status of the flag, where <code>true</code> signifies a "1" and
	 *         <code>false</code> signifies a "0"
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #getFlag(int,byte,byte[])
	 * @see #readDevice()
	 * @see #setFlag(int,byte,boolean)
	 * @see #TIMER_ALARM_SEARCH_FLAG
	 * @see #TEMP_HIGH_SEARCH_FLAG
	 * @see #TEMP_LOW_SEARCH_FLAG
	 * @see #ROLLOVER_ENABLE_FLAG
	 * @see #MISSION_ENABLE_FLAG
	 * @see #MEMORY_CLEAR_ENABLE_FLAG
	 * @see #OSCILLATOR_ENABLE_FLAG
	 * @see #TIMER_ALARM_FLAG
	 * @see #TEMPERATURE_HIGH_FLAG
	 * @see #TEMPERATURE_LOW_FLAG
	 * @see #SAMPLE_IN_PROGRESS_FLAG
	 * @see #MISSION_IN_PROGRESS_FLAG
	 * @see #MEMORY_CLEARED_FLAG
	 * @see #TEMP_CORE_BUSY_FLAG
	 *
	 *
	 */
	public boolean getFlag(int register, byte bitMask) throws OneWireIOException, OneWireException {
		return (this.readByte(register) & bitMask) != 0;
	}

	/**
	 * <p>
	 * Gets the status of the specified flag from the specified register. This
	 * method is the preferred manner of reading the control and status flags.
	 * </p>
	 *
	 * <p>
	 * For more information on valid values for the <code>bitMask</code> parameter,
	 * see the {@link #getFlag(int,byte) getFlag(int,byte)} method.
	 * </p>
	 *
	 * @param register address of register containing the flag (valid values are
	 *                 <code>CONTROL_REGISTER</code> and
	 *                 <code>STATUS_REGISTER</code>)
	 * @param bitMask  the flag to read (see {@link #getFlag(int,byte)
	 *                 getFlag(int,byte)} for available options)
	 * @param state    current state of the device returned from
	 *                 <code>readDevice()</code>
	 *
	 * @return the status of the flag, where <code>true</code> signifies a "1" and
	 *         <code>false</code> signifies a "0"
	 *
	 * @see #getFlag(int,byte)
	 * @see #readDevice()
	 * @see #setFlag(int,byte,boolean,byte[])
	 */
	public boolean getFlag(int register, byte bitMask, byte[] state) {
		return (state[register & 31] & bitMask) != 0;
	}

	/**
	 * <p>
	 * Sets the status of the specified flag in the specified register. If a mission
	 * is in progress a <code>OneWireIOException</code> will be thrown (one cannot
	 * write to the registers while a mission is commencing). This method actually
	 * communicates with the DS1921 Thermocron. To improve performance if you intend
	 * to make multiple calls to this method, first call <code>readDevice()</code>
	 * and use the <code>setFlag(int,byte,boolean,byte[])</code> method instead.
	 * </p>
	 *
	 * <p>
	 * For more information on valid values for the <code>bitMask</code> parameter,
	 * see the {@link #getFlag(int,byte) getFlag(int,byte)} method.
	 * </p>
	 *
	 * @param register  address of register containing the flag (valid values are
	 *                  <code>CONTROL_REGISTER</code> and
	 *                  <code>STATUS_REGISTER</code>)
	 * @param bitMask   the flag to read (see {@link #getFlag(int,byte)
	 *                  getFlag(int,byte)} for available options)
	 * @param flagValue new value for the flag (<code>true</code> is logic "1")
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'. In the case of
	 *                            the DS1921 Thermocron, this could also be due to a
	 *                            currently running mission.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #getFlag(int,byte)
	 * @see #getFlag(int,byte,byte[])
	 * @see #setFlag(int,byte,boolean,byte[])
	 * @see #readDevice()
	 */
	public void setFlag(int register, byte bitMask, boolean flagValue) throws OneWireIOException, OneWireException {

		// check for Mission in Progress flag
		if (this.getFlag(STATUS_REGISTER, MISSION_IN_PROGRESS_FLAG)) {
			throw new OneWireIOException("OneWireContainer21-Cannot write to register while mission is in progress.");
		}

		// read the current flag settings
		var flags = this.readByte(register);

		if (flagValue) {
			flags = (byte) (flags | bitMask);
		} else {
			flags = (byte) (flags & ~bitMask);
		}

		// write the regs back
		this.writeByte(register, flags);
	}

	/**
	 * <p>
	 * Sets the status of the specified flag in the specified register. If a mission
	 * is in progress a <code>OneWireIOException</code> will be thrown (one cannot
	 * write to the registers while a mission is commencing). This method is the
	 * preferred manner of setting the DS1921 status and control flags. The method
	 * <code>writeDevice()</code> must be called to finalize changes to the device.
	 * Note that multiple 'set' methods can be called before one call to
	 * <code>writeDevice()</code>.
	 * </p>
	 *
	 * <p>
	 * For more information on valid values for the <code>bitMask</code> parameter,
	 * see the {@link #getFlag(int,byte) getFlag(int,byte)} method.
	 * </p>
	 *
	 * @param register  address of register containing the flag (valid values are
	 *                  <code>CONTROL_REGISTER</code> and
	 *                  <code>STATUS_REGISTER</code>)
	 * @param bitMask   the flag to read (see {@link #getFlag(int,byte)
	 *                  getFlag(int,byte)} for available options)
	 * @param flagValue new value for the flag (<code>true</code> is logic "1")
	 * @param state     current state of the device returned from
	 *                  <code>readDevice()</code>
	 *
	 * @see #getFlag(int,byte)
	 * @see #getFlag(int,byte,byte[])
	 * @see #setFlag(int,byte,boolean)
	 * @see #readDevice()
	 * @see #writeDevice(byte[])
	 */
	public void setFlag(int register, byte bitMask, boolean flagValue, byte[] state) {
		register = register & 31;

		var flags = state[register];

		if (flagValue) {
			flags = (byte) (flags | bitMask);
		} else {
			flags = (byte) (flags & ~bitMask);
		}

		// write the regs back
		state[register] = flags;
	}

	/**
	 * <p>
	 * Begins this DS1921's mission. If a mission is already in progress, this will
	 * throw a <code>OneWireIOException</code>. The mission will wait the number of
	 * minutes specified by the mission start delay (use
	 * <code>setMissionStartDelay()</code>) before beginning.
	 * </p>
	 *
	 * <p>
	 * Note that this method actually communicates with the DS1921 Thermocron. No
	 * call to <code>writeDevice()</code> is required to finalize mission enabling.
	 * However, some flags (such as the mission start delay) may need to be set with
	 * a call to <code>writeDevice()</code> before the mission is enabled. See the
	 * usage section above for an example of starting a mission.
	 * </p>
	 *
	 * @param sampleRate the number of minutes to wait in between temperature
	 *                   samples (valid values are 1 to 255)
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'. In the case of
	 *                            the DS1921 Thermocron, this could also be due to a
	 *                            currently running mission.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #disableMission()
	 * @see #setMissionStartDelay(int,byte[])
	 * @see #writeDevice(byte[])
	 */
	public void enableMission(int sampleRate) throws OneWireIOException, OneWireException {
		/* check for valid parameters */
		if (sampleRate > 255 || sampleRate < 0) {
			throw new IllegalArgumentException("OneWireContainer21-Sample rate must be 255 minutes or less");
		}

		if (this.getFlag(STATUS_REGISTER, MISSION_IN_PROGRESS_FLAG)) {
			throw new OneWireIOException("OneWireContainer30-Unable to start mission (Mission already in Progress)");
		}

		// read the current register status
		var controlReg = this.readByte(CONTROL_REGISTER);

		// Set the enable mission byte to 0
		controlReg = (byte) (controlReg & 0xEF);

		this.writeByte(CONTROL_REGISTER, controlReg);

		// set the sample rate and let her rip
		this.writeByte(0x20D, (byte) (sampleRate & 0x000000ff));
	}

	/**
	 * Ends this DS1921's running mission. Note that this method actually
	 * communicates with the DS1921 Thermocron. No additional call to
	 * <code>writeDevice(byte[])</code> is required.
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #enableMission(int)
	 */
	public void disableMission() throws OneWireIOException, OneWireException {

		// first read the current register
		var statusReg = this.readByte(STATUS_REGISTER);

		// Set the MIP bit to 0, regardless of whether a mission is commencing
		statusReg = (byte) (statusReg & 0xDF); // set the MIP bit to 0;

		this.writeByte(STATUS_REGISTER, statusReg);
	}

	/**
	 * <p>
	 * Sets the time to wait before starting the mission. The DS1921 will sleep
	 * <code>missionStartDelay</code> minutes after the mission is enabled with
	 * <code>enableMission(int)</code>, then begin taking samples. Only the least
	 * significant 16 bits of <code>missionStartDelay</code> are relevant.
	 * </p>
	 *
	 * <p>
	 * The method <code>writeDevice()</code> must be called to finalize changes to
	 * the device. Note that multiple 'set' methods can be called before one call to
	 * <code>writeDevice()</code>.
	 * </p>
	 *
	 * @param missionStartDelay the time in minutes to delay the first sample
	 * @param state             current state of the device returned from
	 *                          <code>readDevice()</code>
	 *
	 * @see #readDevice()
	 * @see #writeDevice(byte[])
	 * @see #enableMission(int)
	 */
	public void setMissionStartDelay(int missionStartDelay, byte[] state) {
		state[0x12] = (byte) missionStartDelay;
		state[0x13] = (byte) (missionStartDelay >> 8);
	}

	/**
	 * <p>
	 * Clears the memory of any previous mission. The memory must be cleared before
	 * setting up a new mission. If a mission is in progress a
	 * <code>OneWireIOException</code> is thrown.
	 * </p>
	 *
	 * <p>
	 * The Clear Memory command clears the Thermocron's memory at address 220h and
	 * higher. It also clears the sample rate, mission start delay, mission time
	 * stamp, and mission samples counter.
	 * </p>
	 *
	 * <p>
	 * Note that this method actually communicates with the DS1921 Thermocron. No
	 * call to <code>writeDevice(byte[])</code> is necessary to finalize this
	 * activity.
	 * </p>
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'. In the case of
	 *                            the DS1921 Thermocron, this could also be due to a
	 *                            currently running mission.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #enableMission(int)
	 * @see #writeDevice(byte[])
	 */
	public void clearMemory() throws OneWireIOException, OneWireException {
		// added 8/29/2001 by SH - delay necessary so that clock is
		// running before mission is enabled.
		// check to see if the Oscillator is enabled.
		var state = this.readDevice();
		if (this.isClockRunning(state)) {
			// if the oscillator is not enabled, start it
			this.setClockRunEnable(true, state);
			this.writeDevice(state);
			// and give it the required time
			try {
				Thread.sleep(751);
			} catch (InterruptedException ie) {

			}
		}

		// first set the MCLRE bit to 1 in the control register
		this.setFlag(CONTROL_REGISTER, MEMORY_CLEAR_ENABLE_FLAG, true);

		// now send the memory clear command and wait 5 milliseconds
		if (this.doSpeedEnable) {
			this.doSpeed();
		}

		this.adapter.reset();

		if (!this.adapter.select(this.address)) {
			throw new OneWireException("OneWireContainer21-Device not found.");
		}
		this.adapter.putByte(CLEAR_MEMORY_COMMAND);

		try {
			Thread.sleep(5);
		} catch (Exception e) {
			// drain it
		}
	}

	/**
	 * <p>
	 * Gets the clock alarm time settings. The alarm settings used by the Thermocron
	 * are Hour, Minute, Second, and Day of Week. Note that not all values in the
	 * returned <code>java.util.Calendar</code> object are valid. Only four values
	 * in the <code>Calendar</code> should be used. The field names for these values
	 * are:
	 *
	 * <pre>
	 * <code>
	 *      Calendar.DAY_OF_MONTH
	 *      Calendar.HOUR_OF_DAY
	 *      Calendar.MINUTE
	 *      Calendar.SECOND</code>
	 * </pre>
	 * </p>
	 * <p>
	 * The hour is reported in 24-hour format. Use the method
	 * <code>getClockAlarm(byte[])</code> to find out the next time an alarm event
	 * will occur.
	 * </p>
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the alarm clock time and day of the week
	 *
	 * @see #setClockAlarm(int,int,int,int,int,byte[])
	 * @see #readDevice()
	 * @see #getClockAlarm(byte[])
	 */
	public Calendar getAlarmTime(byte[] state) {

		// first get the time
		var time = this.getTime(0x207, state);
		var result = Calendar.getInstance();
		result.set(Calendar.YEAR, 0);
		result.set(Calendar.MONTH, 0);
		result.set(Calendar.DATE, 0);
		result.set(Calendar.HOUR_OF_DAY, time[2]);
		result.set(Calendar.MINUTE, time[1]);
		result.set(Calendar.SECOND, time[0]);

		// Removed by SH - Not J2ME-compatible
		// Calendar result = new GregorianCalendar(0, 0, 0, time [2], time [1],
		// time [0]);

		// now put the day of the week in there
		var dayOfWeek = (byte) (state[0x0A] & 0x07);

		result.set(Calendar.DAY_OF_MONTH, dayOfWeek);

		return result;
	}

	/**
	 * Set the DS1921's alarm clock. Some of the parameters might be unimportant
	 * depending on the alarm frequency setting. For instance, if the alarm
	 * frequency setting is <code>ONCE_PER_MINUTE</code>, then the <code>hour</code>
	 * argument is irrelevant.
	 * </p>
	 *
	 * <p>
	 * Valid values for <code>alarmFrequency</code> are:
	 *
	 * <pre>
	 * <code>
	 *    ONCE_PER_SECOND
	 *    ONCE_PER_MINUTE
	 *    ONCE_PER_HOUR
	 *    ONCE_PER_DAY
	 *    ONCE_PER_WEEK</code>
	 * </pre>
	 * </p>
	 *
	 * <p>
	 * The method <code>writeDevice()</code> must be called to finalize changes to
	 * the device. Note that multiple 'set' methods can be called before one call to
	 * <code>writeDevice()</code>.
	 * </p>
	 *
	 * @param hours          the hour of the day (0-23)
	 * @param minutes        the minute setting (0-59)
	 * @param seconds        the second setting (0-59)
	 * @param day            the day of the week (1-7, 1==Sunday)
	 * @param alarmFrequency frequency that the alarm should occur at
	 * @param state          current state of the device returned from
	 *                       <code>readDevice()</code>
	 *
	 * @see #readDevice()
	 * @see #writeDevice(byte[])
	 * @see #getClockAlarm(byte[])
	 * @see #ONCE_PER_SECOND
	 * @see #ONCE_PER_MINUTE
	 * @see #ONCE_PER_HOUR
	 * @see #ONCE_PER_DAY
	 * @see #ONCE_PER_WEEK
	 */
	public void setClockAlarm(int hours, int minutes, int seconds, int day, int alarmFrequency, byte[] state) {
		this.setTime(0x207, hours, minutes, seconds, false, state);

		state[0x0a] = (byte) day;

		// how many of the MS, MM, MH, MD bytes have
		// 0 as their ms bit???
		var number_0_msb = switch (alarmFrequency) {
		case ONCE_PER_SECOND -> 0;
		case ONCE_PER_MINUTE -> 1;
		case ONCE_PER_HOUR -> 2;
		case ONCE_PER_DAY -> 3;
		case ONCE_PER_WEEK -> 4;
		default -> 4;
		};

		for (var i = 0x07; i < 0x0b; i++) {
			if (number_0_msb > 0) {
				number_0_msb--;

				state[i] = (byte) (state[i] & 0x7f); // make the leading bit 0
			} else {
				state[i] = (byte) (state[i] | 0x80); // make the laeding bit 1
			}
		}
	}

	/**
	 * Returns the rate at which the DS1921 takes temperature samples. This rate is
	 * set when the mission is enabled (in the method
	 * <code>enableMission(int)</code>.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the time, in minutes, between temperature readings
	 *
	 * @see #enableMission(int)
	 * @see #readDevice()
	 */
	public int getSampleRate(byte[] state) {
		return 0x0FF & state[0x0D];
	}

	/**
	 * Determines the number of samples taken on this mission. Only the last 2048
	 * samples appear in the Thermocron's log, though all readings from the current
	 * mission are logged in the histogram.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the number of samples taken in the current mission
	 *
	 * @see #readDevice()
	 * @see #getDeviceSamplesCounter(byte[])
	 */
	public int getMissionSamplesCounter(byte[] state) {
		var low = state[0x1A];
		var medium = state[0x1B];
		var high = state[0x1C];

		return high << 16 & 0x00ff0000 | medium << 8 & 0x0000ff00 | low & 0x000000ff;
	}

	/**
	 * <p>
	 * Determines the total number of samples taken by this Thermocron. This
	 * includes samples taken in past missions. It also includes 'forced' readings.
	 * A 'forced' reading refers to a reading taken when the Thermocron does not
	 * have a running mission and is instructed to read the current temperature.
	 * </p>
	 *
	 * <p>
	 * The DS1921 Thermocron is tested to last for 1 million temperature readings.
	 * </p>
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the total number of measurements taken by this Thermocron
	 *
	 * @see #readDevice()
	 * @see #getMissionSamplesCounter(byte[])
	 */
	public int getDeviceSamplesCounter(byte[] state) {
		var low = state[0x1D];
		var medium = state[0x1E];
		var high = state[0x1F];

		return high << 16 & 0x00ff0000 | medium << 8 & 0x0000ff00 | low & 0x000000ff;
	}

	/**
	 * Returns the date and time that the last mission was started. The values in
	 * the <code>java.util.Calendar</code> object are fully specified. In other
	 * words, the year, month, date, hour, minute, and second are all valid in the
	 * returned object.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the date and time that the last mission was started
	 *
	 * @see #readDevice()
	 */
	public Calendar getMissionTimeStamp(byte[] state) {
		/*
		 * i know here that the mission time stamp does not start at address 214,
		 * however--the mission time stamp starts with minutes, and i have a method to
		 * read the seconds. since i can ignore that in this case, i can go ahead and
		 * 'fake' read the seconds
		 */
		var time_result = this.getTime(0x214, state);
		var date_result = this.getDate(0x217, state);
		var year = date_result[0] % 100;

		// determine the century based on the number of samples taken
		var numberOfCounts = this.getMissionSamplesCounter(state);
		var timeBetweenCounts = this.getSampleRate(state);
		var yearsSinceMissionStart = numberOfCounts * timeBetweenCounts / 525600;

		// get a rough estimate of how long ago this was
		// result = getDateTime(state);
		var offset_result = this.getDate(0x204, state);
		var result_year = offset_result[0];

		// add the century based on this calculation
		// if ((result.get(Calendar.YEAR) - yearsSinceMissionStart) > 1999)
		if (result_year - yearsSinceMissionStart > 1999) {
			year += 2000;
		} else {
			year += 1900;
		}

		// protect against deviations that may cause gross errors
		// if (year > result.get(Calendar.YEAR))
		if (year > result_year) {
			year -= 100;
		}

		var result = Calendar.getInstance();
		result.set(Calendar.YEAR, year);
		result.set(Calendar.MONTH, date_result[1] - 1);
		result.set(Calendar.DATE, date_result[2]);
		result.set(Calendar.HOUR_OF_DAY, time_result[2]);
		result.set(Calendar.MINUTE, time_result[1]);
		// SH - zeroed out the seconds (previously random)
		result.set(Calendar.SECOND, 0);

		return result;
		// removed by SH - not J2ME-compliant
		// new GregorianCalendar(year, date_result [1] - 1,
		// date_result [2], time_result [2],
		// time_result [1]);
	}

	/**
	 * <p>
	 * Helps determine the times for values in a temperature log. If rollover is
	 * enabled, temperature log entries will over-write previous entries once more
	 * than 2048 logs are written. The returned value can be added to the underlying
	 * millisecond value of <code>getMissionTimeStamp()</code> to determine the time
	 * that the 'first' log entry actually occurred.
	 * </p>
	 *
	 * <pre>
	 * <code>
	 *      //ds1921 is a OneWireContainer21
	 *      byte[] state = ds1921.readDevice();
	 *      Calendar c = ds1921.getMissionTimeStamp(state);
	 *      //find the time for the first log entry
	 *      long first_entry = c.getTime().getTime();
	 *      first_entry += ds1921.getFirstLogOffset(state);
	 *      . . .
	 * </code>
	 * </pre>
	 *
	 * <p>
	 * Be cautious of Java's Daylight Savings Time offsets when using this
	 * function--if you use the <code>Date</code> or <code>Calendar</code> class to
	 * print this out, Java may try to automatically format the
	 * <code>java.lang.String</code> to handle Daylight Savings Time, resulting in
	 * offset by 1 hour problems.
	 * </p>
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return milliseconds between the beginning of the mission and the time of the
	 *         first log entry reported from <code>getTemperatureLog()</code>
	 *
	 * @see #readDevice()
	 * @see #getMissionTimeStamp(byte[])
	 * @see #getTemperatureLog(byte[])
	 */
	public long getFirstLogOffset(byte[] state) {
		long counter = this.getMissionSamplesCounter(state);

		if (counter < 2049 || !this.getFlag(CONTROL_REGISTER, ROLLOVER_ENABLE_FLAG, state)) {
			return 0;
		}

		// else we need to figure out when the first sample occurred
		// since there are counter entries, the first entry is (counter - 2048)
		// so if we multiply that times milliseconds between entry,
		// we should be OK
		counter -= 2048;

		// rate is the rate in minutes, must multiply by 60 to be seconds,
		// then by 1000 to be milliseconds
		var rate = this.getSampleRate(state);

		return counter * rate * 1000 * 60;
	}

	/**
	 * <p>
	 * Returns the log of temperature measurements. Each <code>byte</code> in the
	 * returned array is an independent sample. Use the method
	 * <code>decodeTemperature(byte)</code> to get the double value of the encoded
	 * temperature. See the DS1921 datasheet for more on the data's encoding scheme.
	 * The array's length equals the number of measurements taken thus far. The
	 * temperature log can be read while a mission is still in progress.
	 * </p>
	 *
	 * <p>
	 * Note that although this method takes the device state as a parameter, this
	 * method still must communicate directly with the Thermocron to read the log.
	 * </p>
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the DS1921's encoded temperature log
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #decodeTemperature(byte)
	 * @see #readDevice()
	 * @see #getFirstLogOffset(byte[])
	 * @see #getMissionTimeStamp(byte[])
	 */
	public synchronized byte[] getTemperatureLog(byte[] state) throws OneWireIOException, OneWireException {
		byte[] result;

		/* get the number of samples and the rate at which they were taken */
		var numberOfReadings = this.getMissionSamplesCounter(state);

		// used for rollover
		var offsetDepth = 0;

		/* this next line checks the rollover bit and whether a rollover occurred */
		if (this.getFlag(CONTROL_REGISTER, ROLLOVER_ENABLE_FLAG, state) && numberOfReadings > 2048) {

			// offsetDepth holds the number of new readings before we hit older ones
			offsetDepth = numberOfReadings % 2048;
		}

		// the max number of readings STORED is 2048
		if (numberOfReadings > 2048) {
			numberOfReadings = 2048;
		}

		result = new byte[numberOfReadings];

		var offset = 0;

		while (offset < numberOfReadings) {
			this.log.readPageCRC(offset >> 5, false, this.read_log_buffer, offset);

			offset += 32;
		}

		// put the bytes into the output array, but careful for the case
		// where we rolled over that we start in the right place!
		System.arraycopy(this.read_log_buffer, offsetDepth, result, 0, numberOfReadings - offsetDepth);
		System.arraycopy(this.read_log_buffer, 0, result, numberOfReadings - offsetDepth, offsetDepth);

		return result;
	}

	/**
	 * <p>
	 * Returns an array of at most 64 counter bins holding the DS1921 histogram data
	 * (63 bins for the DS1921L-F5X and 64 bins for the DS1921H or DS1921Z). For the
	 * temperature histogram, the DS1921 provides bins that each consist of a
	 * 16-bit, non rolling-over binary counter that is incremented each time a
	 * temperature value acquired during a mission falls into the range of the bin.
	 * The bin to be updated is determined by cutting off the two least significant
	 * bits of the binary temperature value. For example, on a DS1921L-F5X, bin 0
	 * will hold the counter for temperatures ranging from -40 to -38.5 (Celsius)
	 * and lower. Bin 1 is associated with the range of -38 to 36.5 and so on. The
	 * last bin, in this case bin 62, holds temperature values of 84 degrees and
	 * higher. Please see the respective DS1921H or DS1921Z datasheets for their bin
	 * arrangements. The temperature histogram can be read while a mission is still
	 * in progress.
	 * </p>
	 *
	 * @return the 63 temperature counters
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 */
	public int[] getTemperatureHistogram() throws OneWireIOException, OneWireException {
		int[] result;
		if (this.isDS1921HZ) {
			result = new int[64]; // One more bin for the H or Z
		} else {
			result = new int[63];
		}
		var buffer = new byte[128];

		/* read the data first */
		var offset = 0;

		while (offset < 128) {
			this.histogram.readPageCRC(offset >> 5, false, buffer, offset);

			offset += 32;
		}

		int i = 0, j = 0;

		while (i < result.length) {

			// get the 2 byte counter values
			result[i] = buffer[j] & 0x00ff | buffer[j + 1] << 8 & 0xff00;

			i++;

			j += 2;
		}

		return result;
	}

	/**
	 * Returns <code>true</code> if the specified alarm has been triggered. Valid
	 * values for the <code>alarmBit</code> parameter are:<code><pre>
	 *     TEMPERATURE_LOW_ALARM
	 *     TEMPERATURE_HIGH_ALARM
	 *     TIMER_ALARM
	 * </pre></code>
	 *
	 * @param alarmBit the alarm to check
	 * @param state    current state of the device returned from
	 *                 <code>readDevice()</code>
	 *
	 * @return <true> if the specified alarm has been triggered
	 *
	 * @see #TEMPERATURE_LOW_ALARM
	 * @see #TEMPERATURE_HIGH_ALARM
	 * @see #TIMER_ALARM
	 * @see #readDevice()
	 * @see #getAlarmHistory(byte)
	 */
	public boolean getAlarmStatus(byte alarmBit, byte[] state) {
		return (state[STATUS_REGISTER & 31] & alarmBit) != 0;
	}

	/**
	 * <p>
	 * Returns an array containing the alarm log. The DS1921 contains two separate
	 * alarm logs. One for the high temperature alarm and one for the low
	 * temperature alarm. Each log can store up to 12 log entries and each log entry
	 * will record up to 255 consecutive alarm violations.
	 * </p>
	 *
	 * <p>
	 * The returned array is not altered from its representation on the DS1921
	 * Thermocron. It is therefore up to the caller to interpret the data. The
	 * number of logs in this alarm history is equal to the array length divided by
	 * 4, since each entry is 4 bytes. The first three bytes are the number of
	 * samples into the mission that the alarm occurred. The fourth byte is the
	 * number of consecutive samples that violated the alarm. To extract the
	 * starting offset and number of violations from the array:
	 * </p>
	 * <code><pre>
	 *       byte[] data = ds1921.getAlarmHistory(OneWireContainer21.TEMPERATURE_HIGH_ALARM);
	 *       int start_offset;
	 *       int violation_count;
	 *       . . .
	 *       for (int i=0;i < data.length/4; i++)
	 *       {
	 *           start_offset = (data [i * 4] & 0x0ff)
	 *                     | ((data [i * 4 + 1] << 8) & 0x0ff00)
	 *                     | ((data [i * 4 + 2] << 16) & 0x0ff0000);
	 *           violation_count = 0x0ff & data[i*4+3];
	 *
	 *           . . .
	 *
	 *           // note: you may find it useful to multiply start_offset
	 *           //       by getSampleRate() in order to get the number of
	 *           //       minutes into the mission that the violation occurred
	 *           //       on.  You can do the same with violation_count
	 *           //       to determine how long the violation lasted.
	 *       }
	 * </pre></code>
	 *
	 * <p>
	 * Acceptable values for the <code>alarmBit</code> parameter are:
	 *
	 * <pre>
	 * <code>
	 *     TEMPERATURE_LOW_ALARM
	 *     TEMPERATURE_HIGH_ALARM</code>
	 * </pre>
	 * </p>
	 *
	 * @param alarmBit the alarm log to get
	 *
	 * @return the time/duration of the alarm (see above for the structure of the
	 *         array)
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 *
	 * @see #getAlarmStatus(byte,byte[])
	 */
	public byte[] getAlarmHistory(byte alarmBit) throws OneWireIOException, OneWireException {
		var counter = 0;
		var temp_data = new byte[96];
		var offset = 0;

		while (offset < 96) {
			this.alarm.readPageCRC(offset >> 5, false, temp_data, offset);

			offset += 32;
		}

		if (alarmBit == TEMPERATURE_LOW_ALARM) {
			offset = 0;
		} else {
			offset = 48;
		}

		/*
		 * check how many entries there are (each entry consists of 4 bytes) the fourth
		 * byte of each entry is the counter - check if its > 0
		 */
		/* but there can only be a maximum of 12 entries! */
		while (counter < 12 && counter * 4 + 3 + offset < temp_data.length
				&& temp_data[counter * 4 + 3 + offset] != 0) {
			counter++;
		}

		var data = new byte[counter << 2];

		System.arraycopy(temp_data, offset, data, 0, counter << 2);

		return data;
	}

	/**
	 * Retrieves the 1-Wire device sensor state. This state is returned as a byte
	 * array. Pass this byte array to the 'get' and 'set' methods. If the device
	 * state needs to be changed then call the 'writeDevice' to finalize the
	 * changes.
	 *
	 * @return 1-Wire device sensor state
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 */
	@Override
	public byte[] readDevice() throws OneWireIOException, OneWireException {
		var buffer = new byte[32];

		// going to return the register page, 32 bytes
		this.register.readPageCRC(0, false, buffer, 0);

		return buffer;
	}

	/**
	 * Writes the 1-Wire device sensor state that have been changed by 'set'
	 * methods. Only the state registers that changed are updated. This is done by
	 * referencing a field information appended to the state data.
	 *
	 * @param state 1-Wire device sensor state
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 */
	@Override
	public void writeDevice(byte[] state) throws OneWireIOException, OneWireException {
		if (this.getFlag(STATUS_REGISTER, MISSION_IN_PROGRESS_FLAG)) {
			throw new OneWireIOException("OneWireContainer21-Cannot write to registers while mission is in progress.");
		}

		var start = this.updatertc ? 0 : 7;

		this.register.write(start, state, start, 20 - start); // last 12 bytes are read only

		synchronized (this) {
			this.updatertc = false;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////
	//
	// Temperature Interface Functions
	//
	////////////////////////////////////////////////////////////////////////////////////////

	/**
	 * Checks to see if this temperature measuring device has high/low trip alarms.
	 *
	 * @return <code>true</code> if this <code>TemperatureContainer</code> has
	 *         high/low trip alarms
	 *
	 * @see #getTemperatureAlarm
	 * @see #setTemperatureAlarm
	 */
	@Override
	public boolean hasTemperatureAlarms() {
		return true;
	}

	/**
	 * Checks to see if this device has selectable temperature resolution.
	 *
	 * @return <code>true</code> if this <code>TemperatureContainer</code> has
	 *         selectable temperature resolution
	 *
	 * @see #getTemperatureResolution
	 * @see #getTemperatureResolutions
	 * @see #setTemperatureResolution
	 */
	@Override
	public boolean hasSelectableTemperatureResolution() {
		return false;
	}

	/**
	 * Get an array of available temperature resolutions in Celsius.
	 *
	 * @return byte array of available temperature resolutions in Celsius with
	 *         minimum resolution as the first element and maximum resolution as the
	 *         last element
	 *
	 * @see #hasSelectableTemperatureResolution
	 * @see #getTemperatureResolution
	 * @see #setTemperatureResolution
	 */
	@Override
	public double[] getTemperatureResolutions() {
		var d = new double[1];

		d[0] = this.temperatureResolution;

		return d;
	}

	/**
	 * Gets the temperature alarm resolution in Celsius.
	 *
	 * @return temperature alarm resolution in Celsius for this 1-wire device
	 *
	 * @see #hasTemperatureAlarms
	 * @see #getTemperatureAlarm
	 * @see #setTemperatureAlarm
	 *
	 */
	@Override
	public double getTemperatureAlarmResolution() {
		return 1.5;
	}

	/**
	 * Gets the maximum temperature in Celsius.
	 *
	 * @return maximum temperature in Celsius for this 1-wire device
	 *
	 * @see #getMinTemperature()
	 */
	@Override
	public double getMaxTemperature() {
		return this.getOperatingRangeHighTemperature();
	}

	/**
	 * Gets the minimum temperature in Celsius.
	 *
	 * @return minimum temperature in Celsius for this 1-wire device
	 *
	 * @see #getMaxTemperature()
	 */
	@Override
	public double getMinTemperature() {
		return this.getOperatingRangeLowTemperature();
	}

	// --------
	// -------- Temperature I/O Methods
	// --------

	/**
	 * Performs a temperature conversion. Use the <code>state</code> information to
	 * calculate the conversion time.
	 *
	 * @param state byte array with device state information
	 *
	 * @throws OneWireIOException on a 1-Wire communication error such as reading an
	 *                            incorrect CRC from a 1-Wire device. This could be
	 *                            caused by a physical interruption in the 1-Wire
	 *                            Network due to shorts or a newly arriving 1-Wire
	 *                            device issuing a 'presence pulse'. In the case of
	 *                            the DS1921 Thermocron, this could also be due to a
	 *                            currently running mission.
	 * @throws OneWireException   on a communication or setup error with the 1-Wire
	 *                            adapter
	 */
	@Override
	public void doTemperatureConvert(byte[] state) throws OneWireIOException, OneWireException {

		/* check for mission in progress */
		if (this.getFlag(STATUS_REGISTER, MISSION_IN_PROGRESS_FLAG)) {
			throw new OneWireIOException("OneWireContainer21-Cant force " + "temperature read during a mission.");
		}

		/* get the temperature */
		if (this.doSpeedEnable) {
			this.doSpeed(); // we aren't worried about how long this takes...we're sleeping for 750 ms!
		}

		this.adapter.reset();

		if (!this.adapter.select(this.address)) {
			throw new OneWireException("OneWireContainer21-Device not found!");
		}
		// perform the temperature conversion
		this.adapter.putByte(CONVERT_TEMPERATURE_COMMAND);

		try {
			Thread.sleep(750);
		} catch (InterruptedException e) {
		}

		// grab the temperature
		state[0x11] = this.readByte(0x211);
	}

	// --------
	// -------- Temperature 'get' Methods
	// --------

	/**
	 * Gets the temperature value in Celsius from the <code>state</code> data
	 * retrieved from the <code>readDevice()</code> method.
	 *
	 * @param state byte array with device state information
	 *
	 * @return temperature in Celsius from the last
	 *         <code>doTemperatureConvert()</code>
	 */
	@Override
	public double getTemperature(byte[] state) {
		return this.decodeTemperature(state[0x11]);
	}

	/**
	 * Gets the specified temperature alarm value in Celsius from the
	 * <code>state</code> data retrieved from the <code>readDevice()</code> method.
	 *
	 * @param alarmType valid value: <code>ALARM_HIGH</code> or
	 *                  <code>ALARM_LOW</code>
	 * @param state     byte array with device state information
	 *
	 * @return temperature alarm trip values in Celsius for this 1-wire device
	 *
	 * @see #hasTemperatureAlarms
	 * @see #setTemperatureAlarm
	 */
	@Override
	public double getTemperatureAlarm(int alarmType, byte[] state) {
		if (alarmType == TEMPERATURE_HIGH_ALARM || alarmType == ALARM_HIGH) {
			return this.decodeTemperature(state[0x0c]);
		}
		return this.decodeTemperature(state[0x0b]);
	}

	/**
	 * Gets the current temperature resolution in Celsius from the
	 * <code>state</code> data retrieved from the <code>readDevice()</code> method.
	 *
	 * @param state byte array with device state information
	 *
	 * @return temperature resolution in Celsius for this 1-wire device
	 *
	 * @see #hasSelectableTemperatureResolution
	 * @see #getTemperatureResolutions
	 * @see #setTemperatureResolution
	 */
	@Override
	public double getTemperatureResolution(byte[] state) {
		return this.temperatureResolution;
	}

	// --------
	// -------- Temperature 'set' Methods
	// --------

	/**
	 * Sets the temperature alarm value in Celsius in the provided
	 * <code>state</code> data. Use the method <code>writeDevice()</code> with this
	 * data to finalize the change to the device.
	 *
	 * @param alarmType  valid value: <code>ALARM_HIGH</code> or
	 *                   <code>ALARM_LOW</code>
	 * @param alarmValue alarm trip value in Celsius
	 * @param state      byte array with device state information
	 *
	 * @see #hasTemperatureAlarms
	 * @see #getTemperatureAlarm
	 */
	@Override
	public void setTemperatureAlarm(int alarmType, double alarmValue, byte[] state) {
		var histogramLow = this.getHistogramLowTemperature();
		var histogramHigh = this.getPhysicalRangeHighTemperature()
				+ (this.getHistogramBinWidth() - this.getTemperatureResolution());
		var alarm = this.encodeTemperature(alarmValue);

		// take special care of top and bottom of temperature ranges for the different
		// types of thermochrons.
		if (this.isDS1921HZ) {
			if (alarmValue < histogramLow) {
				alarm = 0;
			}

			if (alarmValue > histogramHigh) {
				alarm = (byte) 0xFF; // maximum value stand for the histogram high temperature
			}
		} else {
			if (alarmValue < -40.0) {
				alarm = 0;
			}

			if (alarmValue > 85.0) {
				alarm = (byte) 0xfa; // maximum value stands for 85.0 C
			}
		}

		if (alarmType == TEMPERATURE_HIGH_ALARM || alarmType == ALARM_HIGH) {
			state[0x0c] = alarm;
		} else {
			state[0x0b] = alarm;
		}
	}

	/**
	 * Sets the current temperature resolution in Celsius in the provided
	 * <code>state</code> data. Use the method <code>writeDevice()</code> with this
	 * data to finalize the change to the device.
	 *
	 * @param resolution temperature resolution in Celsius
	 * @param state      byte array with device state information
	 *
	 * @throws OneWireException if the device does not support selectable
	 *                          temperature resolution
	 *
	 * @see #hasSelectableTemperatureResolution
	 * @see #getTemperatureResolution
	 * @see #getTemperatureResolutions
	 */
	@Override
	public void setTemperatureResolution(double resolution, byte[] state) throws OneWireException {
		throw new OneWireException("Selectable Temperature Resolution Not Supported");
	}

	////////////////////////////////////////////////////////////////////////////////////////
	//
	// Clock Interface Functions
	//
	////////////////////////////////////////////////////////////////////////////////////////

	/**
	 * Checks to see if the clock has an alarm feature.
	 *
	 * @return true if the Real-Time clock has an alarm
	 *
	 * @see #getClockAlarm(byte[])
	 * @see #isClockAlarmEnabled(byte[])
	 * @see #isClockAlarming(byte[])
	 * @see #setClockAlarm(long,byte[])
	 * @see #setClockAlarmEnable(boolean,byte[])
	 */
	@Override
	public boolean hasClockAlarm() {
		return true;
	}

	/**
	 * Checks to see if the clock can be disabled.
	 *
	 * @return true if the clock can be enabled and disabled
	 *
	 * @see #isClockRunning(byte[])
	 * @see #setClockRunEnable(boolean,byte[])
	 */
	@Override
	public boolean canDisableClock() {
		return true;
	}

	/**
	 * Gets the clock resolution in milliseconds
	 *
	 * @return the clock resolution in milliseconds
	 */
	@Override
	public long getClockResolution() {
		return 1000;
	}

	// --------
	// -------- Clock 'get' Methods
	// --------

	/**
	 * Extracts the Real-Time clock value in milliseconds.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return the time represented in this clock in milliseconds since 1970
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #setClock(long,byte[])
	 */
	@Override
	public long getClock(byte[] state) {

		/* grab the time (at location 200, date at 204) */
		var time = this.getTime(0x200, state);
		var date = this.getDate(0x204, state);

		// date[1] - 1 because Java months are 0 offset
		// date[0] - 1900 because Java years are from 1900
		// Date d = new Date(date[0]-1900, date[1]-1, date[2], time[2], time[1],
		// time[0]);
		var result = Calendar.getInstance();
		result.set(Calendar.YEAR, date[0]);
		result.set(Calendar.MONTH, date[1] - 1);
		result.set(Calendar.DATE, date[2]);
		result.set(Calendar.HOUR_OF_DAY, time[2]);
		result.set(Calendar.MINUTE, time[1]);
		result.set(Calendar.SECOND, time[0]);

		// removed by SH - not J2ME-compliant
		// Calendar d = new GregorianCalendar(date [0], date [1] - 1, date [2],
		// time [2], time [1], time [0]);

		return result.getTime().getTime();
	}

	/**
	 * Extracts the clock alarm value for the Real-Time clock. In the case of the
	 * DS1921 Thermocron, this is the time that the next periodic alarm event will
	 * occur.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return milliseconds since 1970 that the clock alarm is set to
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #hasClockAlarm()
	 * @see #isClockAlarmEnabled(byte[])
	 * @see #isClockAlarming(byte[])
	 * @see #setClockAlarm(long,byte[])
	 * @see #setClockAlarmEnable(boolean,byte[])
	 */
	@Override
	public long getClockAlarm(byte[] state) {

		// first get the normal real time clock
		var time = this.getTime(0x200, state);
		var date = this.getDate(0x204, state);

		// date[0] = year
		// date[1] = month
		// date[2] = date
		// time[2] = hour
		// time[1] = minute
		// time[0] = second
		// date[1] - 1 because Java does funky months from offset 0
		var c = Calendar.getInstance();
		c.set(Calendar.YEAR, date[0]);
		c.set(Calendar.MONTH, date[1] - 1);
		c.set(Calendar.DATE, date[2]);
		c.set(Calendar.HOUR_OF_DAY, time[2]);
		c.set(Calendar.MINUTE, time[1]);
		c.set(Calendar.SECOND, time[0]);

		// removed by SH - not J2ME-compliant
		// Calendar c = new GregorianCalendar(date [0], date [1] - 1, date [2],
		// time [2], time [1], time [0]);

		// get the seconds into the day we are at
		var time_into_day = time[0] + 60 * time[1] + 60 * 60 * time[2];

		// now lets get the alarm specs
		var a_time = this.getTime(0x207, state);

		// get the seconds into the day the alarm is at
		var a_time_into_day = a_time[0] + 60 * a_time[1] + 60 * 60 * a_time[2];

		// now put the day of the week in there
		var dayOfWeek = (byte) (state[0x0A] & 0x07);

		if (dayOfWeek == 0) {
			dayOfWeek++;
		}

		var MS = (byte) (state[0x07] >>> 7 & 0x01);
		var MM = (byte) (state[0x08] >>> 7 & 0x01);
		var MH = (byte) (state[0x09] >>> 7 & 0x01);
		var MD = (byte) (state[0x0A] >>> 7 & 0x01);

		var temp_time = 0L;
		var MILLIS_PER_DAY = 1000 * 60 * 60 * 24;

		switch (MS + MM + MH + MD) {
		case 4: // ONCE_PER_SECOND
			c.add(Calendar.SECOND, 1);
			break;
		case 3: // ONCE_PER_MINUTE
			if (!(a_time_into_day < time_into_day)) {
				c.add(Calendar.MINUTE, 1);
			}

			c.set(Calendar.SECOND, a_time[0]);
			break;
		case 2: // ONCE_PER_HOUR
			if (!(a_time_into_day < time_into_day)) {
				c.add(Calendar.HOUR_OF_DAY, 1); // will occur again next hour
			}

			c.set(Calendar.SECOND, a_time[0]);
			c.set(Calendar.MINUTE, a_time[1]);
			break;
		case 1: // ONCE_PER_DAY
			c.set(Calendar.SECOND, a_time[0]);
			c.set(Calendar.MINUTE, a_time[1]);
			c.set(Calendar.HOUR_OF_DAY, a_time[2]);

			if (a_time_into_day < time_into_day) {
				c.add(Calendar.DATE, 1); // will occur again tomorrow
			}
			break;
		default:
		case 0: // ONCE_PER_WEEK
			c.set(Calendar.SECOND, a_time[0]);
			c.set(Calendar.MINUTE, a_time[1]);

			// c.set(c.AM_PM, (a_time[2] > 11) ? c.PM : c.AM);
			c.set(Calendar.HOUR_OF_DAY, a_time[2]);

			/*
			 * oh no!!! TINI doesn't like calls to Calendar.roll() and Calendar.add()! we
			 * could be stuck here forever!
			 *
			 * if (dayOfWeek == c.get(c.DAY_OF_WEEK)) {
			 *
			 * //has alarm already occurred today? if ((a_time_into_day < time_into_day))
			 * //alarm has occurred c.add(c.DATE, 7); //will occur again next week } else {
			 *
			 * //roll the day of the week until it matches while (dayOfWeek !=
			 * c.get(c.DAY_OF_WEEK)) c.roll(c.DATE, true); }
			 */
			temp_time = c.getTime().getTime();

			if (dayOfWeek == c.get(Calendar.DAY_OF_WEEK)) {

				// has alarm already occurred today?
				if (a_time_into_day < time_into_day) {
					temp_time += 7 * MILLIS_PER_DAY; // will occur again next week
				}
			} else {

				// roll the day of the week until it matches
				var cdayofweek = c.get(Calendar.DAY_OF_WEEK);

				while (dayOfWeek % 7 != cdayofweek++ % 7) {
					temp_time += MILLIS_PER_DAY;
				}
				// c.roll(c.DATE, true);
			}

			return temp_time;
		}

		return c.getTime().getTime(); // c->getTime returns Date, Date->getTime returns long
	}

	/**
	 * Checks if the clock alarm flag has been set. This will occur when the value
	 * of the Real-Time clock equals the value of the clock alarm.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return true if the Real-Time clock is alarming
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #hasClockAlarm()
	 * @see #isClockAlarmEnabled(byte[])
	 * @see #getClockAlarm(byte[])
	 * @see #setClockAlarm(long,byte[])
	 * @see #setClockAlarmEnable(boolean,byte[])
	 */
	@Override
	public boolean isClockAlarming(byte[] state) {
		return (state[STATUS_REGISTER & 31] & TIMER_ALARM) != 0;
	}

	/**
	 * Checks if the clock alarm is enabled.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return true if clock alarm is enabled
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #hasClockAlarm()
	 * @see #isClockAlarming(byte[])
	 * @see #getClockAlarm(byte[])
	 * @see #setClockAlarm(long,byte[])
	 * @see #setClockAlarmEnable(boolean,byte[])
	 */
	@Override
	public boolean isClockAlarmEnabled(byte[] state) {
		return (state[CONTROL_REGISTER & 31] & TIMER_ALARM_SEARCH_FLAG) != 0;
	}

	/**
	 * Checks if the device's oscillator is enabled. The clock will not increment if
	 * the clock oscillator is not enabled.
	 *
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @return true if the clock is running
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#readDevice()
	 * @see #canDisableClock()
	 * @see #setClockRunEnable(boolean,byte[])
	 */
	@Override
	public boolean isClockRunning(byte[] state) {

		// checks for equal to 0 since active low means clock is running
		return (state[CONTROL_REGISTER & 31] & OSCILLATOR_ENABLE_FLAG) == 0;
	}

	// --------
	// -------- Clock 'set' Methods
	// --------

	/**
	 * Sets the Real-Time clock. The method <code>writeDevice()</code> must be
	 * called to finalize changes to the device. Note that multiple 'set' methods
	 * can be called before one call to <code>writeDevice()</code>.
	 *
	 * @param time  new value for the Real-Time clock, in milliseconds since January
	 *              1, 1970
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#writeDevice(byte[])
	 * @see #getClock(byte[])
	 */
	@Override
	public void setClock(long time, byte[] state) {
		var x = new Date(time);
		var d = Calendar.getInstance();

		// removed by SH - not J2ME-compliant
		// Calendar d = new GregorianCalendar();

		d.setTime(x);
		this.setTime(0x200, d.get(Calendar.HOUR_OF_DAY), d.get(Calendar.MINUTE), d.get(Calendar.SECOND), false, state);
		this.setDate(d.get(Calendar.YEAR), d.get(Calendar.MONTH) + 1, d.get(Calendar.DATE), state);

		synchronized (this) {
			this.updatertc = true;
		}
	}

	/**
	 * Sets the clock alarm. The method <code>writeDevice()</code> must be called to
	 * finalize changes to the device. Note that multiple 'set' methods can be
	 * called before one call to <code>writeDevice()</code>. Also note that not all
	 * clock devices have alarms. Check to see if this device has alarms first by
	 * calling the <code>hasClockAlarm()</code> method.
	 *
	 * @param time  - new value for the Real-Time clock alarm, in milliseconds since
	 *              January 1, 1970
	 * @param state current state of the device returned from
	 *              <code>readDevice()</code>
	 *
	 * @throws OneWireException if this device does not have clock alarms
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#writeDevice(byte[])
	 * @see #hasClockAlarm()
	 * @see #isClockAlarmEnabled(byte[])
	 * @see #getClockAlarm(byte[])
	 * @see #isClockAlarming(byte[])
	 * @see #setClockAlarmEnable(boolean,byte[])
	 */
	@Override
	public void setClockAlarm(long time, byte[] state) throws OneWireException {

		// can't do this because we need more info on the alarm
		throw new OneWireException("Cannot set the DS1921 Clock Alarm through the Clock interface.");
	}

	/**
	 * Enables or disables the oscillator, turning the clock 'on' and 'off'. The
	 * method <code>writeDevice()</code> must be called to finalize changes to the
	 * device. Note that multiple 'set' methods can be called before one call to
	 * <code>writeDevice()</code>. Also note that not all clock devices can disable
	 * their oscillators. Check to see if this device can disable its oscillator
	 * first by calling the <code>canDisableClock()</code> method.
	 *
	 * @param runEnable true to enable the clock oscillator
	 * @param state     current state of the device returned from
	 *                  <code>readDevice()</code>
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#writeDevice(byte[])
	 * @see #canDisableClock()
	 * @see #isClockRunning(byte[])
	 */
	@Override
	public void setClockRunEnable(boolean runEnable, byte[] state) {

		// the oscillator enable is active low
		this.setFlag(CONTROL_REGISTER, OSCILLATOR_ENABLE_FLAG, !runEnable, state);
	}

	/**
	 * Enables or disables the clock alarm. The method <code>writeDevice()</code>
	 * must be called to finalize changes to the device. Note that multiple 'set'
	 * methods can be called before one call to <code>writeDevice()</code>. Also
	 * note that not all clock devices have alarms. Check to see if this device has
	 * alarms first by calling the <code>hasClockAlarm()</code> method.
	 *
	 * @param alarmEnable true to enable the clock alarm
	 * @param state       current state of the device returned from
	 *                    <code>readDevice()</code>
	 *
	 * @see com.dalsemi.onewire.container.OneWireSensor#writeDevice(byte[])
	 * @see #hasClockAlarm()
	 * @see #isClockAlarmEnabled(byte[])
	 * @see #getClockAlarm(byte[])
	 * @see #setClockAlarm(long,byte[])
	 * @see #isClockAlarming(byte[])
	 */
	@Override
	public void setClockAlarmEnable(boolean alarmEnable, byte[] state) {
		this.setFlag(CONTROL_REGISTER, TIMER_ALARM_SEARCH_FLAG, alarmEnable, state);
	}
}
// CHECKSTYLE:ON
