org.rifidi.edge.adapter.llrp.LLRPReaderSession.java Source code

Java tutorial

Introduction

Here is the source code for org.rifidi.edge.adapter.llrp.LLRPReaderSession.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Transcends, LLC.
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU 
 * General Public License as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version. This program is distributed in the hope that it will 
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You 
 * should have received a copy of the GNU General Public License along with this program; if not, 
 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 
 * USA. 
 * http://www.gnu.org/licenses/gpl-2.0.html
 *******************************************************************************/
package org.rifidi.edge.adapter.llrp;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.mina.common.RuntimeIOException;
import org.jdom.Document;
import org.llrp.ltk.exceptions.InvalidLLRPMessageException;
import org.llrp.ltk.generated.LLRPMessageFactory;
import org.llrp.ltk.generated.enumerations.AccessReportTriggerType;
import org.llrp.ltk.generated.enumerations.AccessSpecState;
import org.llrp.ltk.generated.enumerations.AccessSpecStopTriggerType;
import org.llrp.ltk.generated.enumerations.AirProtocols;
import org.llrp.ltk.generated.enumerations.C1G2LockDataField;
import org.llrp.ltk.generated.enumerations.C1G2LockPrivilege;
import org.llrp.ltk.generated.enumerations.GetReaderCapabilitiesRequestedData;
import org.llrp.ltk.generated.enumerations.GetReaderConfigRequestedData;
import org.llrp.ltk.generated.enumerations.StatusCode;
import org.llrp.ltk.generated.interfaces.AccessCommandOpSpec;
import org.llrp.ltk.generated.interfaces.AccessCommandOpSpecResult;
import org.llrp.ltk.generated.messages.ADD_ACCESSSPEC;
import org.llrp.ltk.generated.messages.ADD_ACCESSSPEC_RESPONSE;
import org.llrp.ltk.generated.messages.DELETE_ACCESSSPEC;
import org.llrp.ltk.generated.messages.ENABLE_ACCESSSPEC;
import org.llrp.ltk.generated.messages.GET_READER_CAPABILITIES;
import org.llrp.ltk.generated.messages.GET_READER_CONFIG;
import org.llrp.ltk.generated.messages.GET_ROSPECS;
import org.llrp.ltk.generated.messages.RO_ACCESS_REPORT;
import org.llrp.ltk.generated.messages.SET_READER_CONFIG;
import org.llrp.ltk.generated.messages.SET_READER_CONFIG_RESPONSE;
import org.llrp.ltk.generated.parameters.AccessCommand;
import org.llrp.ltk.generated.parameters.AccessReportSpec;
import org.llrp.ltk.generated.parameters.AccessSpec;
import org.llrp.ltk.generated.parameters.AccessSpecStopTrigger;
import org.llrp.ltk.generated.parameters.C1G2BlockWrite;
import org.llrp.ltk.generated.parameters.C1G2Lock;
import org.llrp.ltk.generated.parameters.C1G2LockPayload;
import org.llrp.ltk.generated.parameters.C1G2Read;
import org.llrp.ltk.generated.parameters.C1G2TagSpec;
import org.llrp.ltk.generated.parameters.C1G2TargetTag;
import org.llrp.ltk.generated.parameters.C1G2Write;
import org.llrp.ltk.generated.parameters.EPCData;
import org.llrp.ltk.generated.parameters.EPC_96;
import org.llrp.ltk.generated.parameters.TagReportData;
import org.llrp.ltk.net.LLRPConnection;
import org.llrp.ltk.net.LLRPConnectionAttemptFailedException;
import org.llrp.ltk.net.LLRPConnector;
import org.llrp.ltk.net.LLRPEndpoint;
import org.llrp.ltk.types.Bit;
import org.llrp.ltk.types.BitArray_HEX;
import org.llrp.ltk.types.LLRPMessage;
import org.llrp.ltk.types.TwoBitField;
import org.llrp.ltk.types.UnsignedInteger;
import org.llrp.ltk.types.UnsignedShort;
import org.llrp.ltk.types.UnsignedShortArray_HEX;
import org.llrp.ltk.util.Util;
import org.rifidi.edge.adapter.llrp.commands.internal.LLRPReset;
import org.rifidi.edge.api.SessionStatus;
import org.rifidi.edge.notification.NotifierService;
import org.rifidi.edge.notification.ReadCycle;
import org.rifidi.edge.sensors.AbstractCommandConfiguration;
import org.rifidi.edge.sensors.AbstractSensor;
import org.rifidi.edge.sensors.Command;
import org.rifidi.edge.sensors.TimeoutCommand;
import org.rifidi.edge.sensors.sessions.AbstractSensorSession;

/**
 * This class represents a session with an LLRP reader. It handles connecting
 * and disconnecting, as well as receiving tag data.
 * 
 * @author Matthew Dean
 */
public class LLRPReaderSession extends AbstractSensorSession implements LLRPEndpoint {

    /** Logger for this class. */
    private static final Log logger = LogFactory.getLog(LLRPReaderSession.class);

    private volatile LLRPConnection connection = null;
    /** Service used to send out notifications */
    private volatile NotifierService notifierService;
    /** The ID of the reader this session belongs to */
    private final String readerID;

    private final String readerConfigPath;

    public Long lastTagTimestamp;

    /** Ok, because only accessed from synchronized block */
    int messageID = 1;
    int maxConAttempts = -1;
    int reconnectionInterval = -1;
    private AtomicBoolean timingOut = new AtomicBoolean(false);

    /** atomic boolean that is true if we are inside the connection attempt loop */
    private AtomicBoolean connectingLoop = new AtomicBoolean(false);
    /** LLRP host */
    private String host;
    /** LLRP port */
    private int port;

    /** Indicates if this session is in the middle of LLRP encode operations **/
    private boolean isRunningLLRPEncoding = false;

    /** the EPC Tag Data to perform operation on **/
    private String targetEpc;

    /**
     * the Tag Mask to perform operation on (0 is wildcard match, F is Exact
     * Match)
     **/
    private String tagMask;

    /**
     * the duration of time to attempt operation (O is infinite until operation
     * occurs, set in milliseconds)
     **/
    private Integer operationsTimeout;

    /** Access Password (Hex Value) **/
    private String accessPwd;

    /** Old access Password (Hex Value) **/
    private String oldAccessPwd;

    /** Kill Password (Hex Value) **/
    private String killPwd;

    /**
     * value to perform a Kill Password Lock Operation (values can be Read_Write
     * (which Locks), Perma_Lock, Unlock)
     **/
    private int killPwdLockPrivilege;

    /**
     * value to perform an Access Password Lock Operation (values can be
     * Read_Write (which Locks), Perma_Lock, Unlock)
     **/
    private int accessPwdLockPrivilege;

    /**
     * value to perform an EPC Lock Operation (values can be Read_Write (which
     * Locks), Perma_Lock, Unlock)
     **/
    private int epcLockPrivilege;

    /**
     * value to perform an user memory lock Operation (values can be Read_Write (which
     * Locks), Perma_Lock, Unlock)
     **/
    private int userMemoryLockPrivilege;

    /** Default target Epc value if there is no property read from jvm **/
    public static final String DEFAULT_TARGET_EPC = "000000000000000000000000";

    /**
     * Default tag mask value if there is no property read from jvm. (0 is
     * wildcard match, F is Exact Match)
     **/
    public static final String DEFAULT_TAG_MASK = "000000000000000000000000";

    /**
     * Default operations timeout value if there is no property read from jvm. O
     * is infinite until operation occurs, set in milliseconds
     **/
    public static final int DEFAULT_OPERATIONS_TIMEOUT = 0;

    /**
     * Default access password value if there is no property read from jvm. (Hex
     * Value)
     **/
    public static final String DEFAULT_ACCESS_PASSWORD = "0";

    /**
     * Default old access password value if there is no property read from jvm.
     * (Hex Value)
     **/
    public static final String DEFAULT_OLD_ACCESS_PASSWORD = "0";

    /**
     * Default kill password value if there is no property read from jvm. (Hex
     * Value)
     **/
    public static final String DEFAULT_KILL_PASSWORD = "0";

    /**
     * Default kill password lock privilege if there is no property read from
     * jvm. (values can be Read_Write (which Locks), Perma_Lock, Unlock)
     **/
    public static final int DEFAULT_KILL_PASSWORD_LOCK_PRIVILEGE = C1G2LockPrivilege.Read_Write;

    /**
     * Default access password lock privilege if there is no property read from
     * jvm. (values can be Read_Write (which Locks), Perma_Lock, Unlock)
     **/
    public static final int DEFAULT_ACCESS_PASSWORD_LOCK_PRIVILEGE = C1G2LockPrivilege.Read_Write;

    /**
     * Default EPC lock privilege if there is no property read from jvm. (values
     * can be Read_Write (which Locks), Perma_Lock, Unlock)
     **/
    public static final int DEFAULT_EPC_LOCK_PRIVILEGE = C1G2LockPrivilege.Read_Write;

    /**
     * Default user memory lock privilege if there is no property read from jvm. (values
     * can be Read_Write (which Locks), Perma_Lock, Unlock)
     **/
    public static final int DEFAULT_USER_MEMORY_LOCK_PRIVILEGE = C1G2LockPrivilege.Read_Write;

    /** Expected value for Read_Write lock privilege taken from jvm **/
    public final static String READ_WRITE_LOCK_PRIVILEGE = "Read_Write";

    /** Expected value for Perma_Lock lock privilege taken from jvm **/
    public final static String PERMA_LOCK_PRIVILEGE = "Perma_Lock";

    /** Expected value for Unlock lock privilege taken from jvm **/
    public final static String UNLOCK_PRIVILEGE = "Unlock";

    private static final int WRITE_EPC_ACCESSSPEC_ID = 101;
    private static final UnsignedShort WRITE_EPC_OPSPEC_ID = new UnsignedShort(102);

    private static final int WRITE_KILLPASS_ACCESSSPEC_ID = 103;
    private static final UnsignedShort WRITE_KILLPASS_OPSPEC_ID = new UnsignedShort(104);

    private static final int WRITE_ACCESSPASS_ACCESSSPEC_ID = 105;
    private static final UnsignedShort WRITE_ACCESSPASS_OPSPEC_ID = new UnsignedShort(106);

    private static final int LOCK_EPC_ACCESSSPEC_ID = 107;
    private static final UnsignedShort LOCK_EPC_OPSPEC_ID = new UnsignedShort(108);

    private static final int LOCK_KILLPASS_ACCESSSPEC_ID = 109;
    private static final UnsignedShort LOCK_KILLPASS_OPSPEC_ID = new UnsignedShort(110);

    private static final int LOCK_ACCESSPASS_ACCESSSPEC_ID = 111;
    private static final UnsignedShort LOCK_ACCESSPASS_OPSPEC_ID = new UnsignedShort(112);

    private static final int READ_EPC_ACCESSSPEC_ID = 113;
    private static final UnsignedShort READ_EPC_OPSPEC_ID = new UnsignedShort(114);

    private static final int READ_ACCESSPASS_ACCESSSPEC_ID = 115;
    private static final UnsignedShort READ_ACCESSPASS_OPSPEC_ID = new UnsignedShort(116);

    private static final int READ_KILLPASS_ACCESSSPEC_ID = 117;
    private static final UnsignedShort READ_KILLPASS_OPSPEC_ID = new UnsignedShort(118);

    private static final int READ_USER_MEMORY_ACCESSSPEC_ID = 119;
    private static final UnsignedShort READ_USER_MEMORY_OPSPEC_ID = new UnsignedShort(120);

    private static final int WRITE_USER_MEMORY_ACCESSSPEC_ID = 121;
    private static final UnsignedShort WRITE_USER_MEMORY_OPSPEC_ID = new UnsignedShort(122);

    private static final int LOCK_USER_MEMORY_ACCESSSPEC_ID = 123;
    private static final UnsignedShort LOCK_USER_MEMORY_OPSPEC_ID = new UnsignedShort(124);

    private static final int READ_MEMORY_BANK_ACCESSSPEC_ID = 125;
    private static final UnsignedShort READ_MEMORY_BANK_OPSEC_ID = new UnsignedShort(126);

    /** Operation names **/
    public enum LLRP_OPERATION_CODE {
        LLRPEPCWrite, LLRPEPCRead, LLRPAccessPasswordWrite, LLRPAccessPasswordValidate, LLRPKillPasswordRead, LLRPKillPasswordWrite, LLRPEPCLock, LLRPKillPasswordLock, LLRPAccessPasswordLock, LLRPUserMemoryLock, LLRPUserMemoryRead, LLRPUserMemoryWrite, LLRPTidRead, LLRPMemoryBankRead
    };

    /** Object to keep track of the operations and responses on each one **/
    private LLRPOperationTracker llrpOperationTracker;

    /**
     * The block length the tag on this session must satisfy. For example for
     * EPC it needs to split in blocks of 4: /0A50708041A1B1D1A1B1E238 so we
     * have blocks like this 0A50 7080 41A1 B1D1 A1B1 E238 to set the data on
     * tag Default is 4
     **/
    private int writeDataBlockLength = 4;

    /** Quality of service level for mqtt, example: 2 **/
    private int mqttQos;

    /** mqtt broker url, example: tcp://localhost:1883 **/
    private String mqttBroker;

    /** mqtt client id to publish messages on queue **/
    private String mqttClientId;

    private boolean executeOperationsInAsynchronousMode;

    /** default number of blocks for EPC to be read **/
    public static final int DEFAULT_EPC_WORD_COUNT = 6;

    /** default number of blocks for user memory to be read **/
    public static final int DEFAULT_USER_MEMORY_WORD_COUNT = 2;

    /** number of blocks to be read, for EPC or user memory **/
    private int wordCount;
    /** memblock to read **/
    private int membank1;
    private int membank2;

    /** data to be set in user memory **/
    private String userMemoryData;

    private Map<Integer, Integer> rssiFilterMap = null;

    private void setRSSIFilterMap(String rssiFilter) {
        try {
            rssiFilterMap = new HashMap<Integer, Integer>();
            String[] pairs = rssiFilter.split("\\|");
            for (String pair : pairs) {
                String[] s = pair.split(":");
                Integer ant = Integer.parseInt(s[0]);
                Integer rssi = Integer.parseInt(s[1]);
                rssiFilterMap.put(ant, rssi);
            }
            if (rssiFilterMap.size() == 0) {
                rssiFilterMap = null;
            } else if (rssiFilterMap.size() == 1) {
                if (rssiFilterMap.containsKey(0) && rssiFilterMap.get(0).equals(0.0f)) {
                    rssiFilterMap = null;
                }
            }
        } catch (Exception e) {
            // Any problems and we disable the filter
            rssiFilterMap = null;
        }
    }

    /**
     * @return the userMemoryData
     */
    public String getUserMemoryData() {
        return userMemoryData;
    }

    /**
     * @param userMemoryData the userMemoryData to set
     */
    public void setUserMemoryData(String userMemoryData) {
        this.userMemoryData = userMemoryData;
    }

    /**
     * @return the wordCount
     */
    public int getWordCount() {
        return wordCount;
    }

    /**
     * @param wordCount the wordCount to set
     */
    public void setWordCount(int wordCount) {
        this.wordCount = wordCount;
    }

    public void setMemBank1(int membank1) {
        this.membank1 = membank1;
    }

    public void setMemBank2(int membank2) {
        this.membank2 = membank2;
    }

    /**
     * @return the executeOperationsInAsynchronousMode
     */
    public boolean isExecuteOperationsInAsynchronousMode() {
        return executeOperationsInAsynchronousMode;
    }

    /**
     * @param executeOperationsInAsynchronousMode
     *            the executeOperationsInAsynchronousMode to set
     */
    public void setExecuteOperationsInAsynchronousMode(boolean executeOperationsInAsynchronousMode) {
        this.executeOperationsInAsynchronousMode = executeOperationsInAsynchronousMode;
    }

    /**
     * @return the mqttQos
     */
    public int getMqttQos() {
        return mqttQos;
    }

    /**
     * @param mqttQos
     *            the mqttQos to set
     */
    public void setMqttQos(int mqttQos) {
        this.mqttQos = mqttQos;
    }

    /**
     * @return the mqttBroker
     */
    public String getMqttBroker() {
        return mqttBroker;
    }

    /**
     * @param mqttBroker
     *            the mqttBroker to set
     */
    public void setMqttBroker(String mqttBroker) {
        this.mqttBroker = mqttBroker;
    }

    /**
     * @return the mqttClientId
     */
    public String getMqttClientId() {
        return mqttClientId;
    }

    /**
     * @param mqttClientId
     *            the mqttClientId to set
     */
    public void setMqttClientId(String mqttClientId) {
        this.mqttClientId = mqttClientId;
    }

    /**
     * @return the writeDataBlockLength
     */
    public int getWriteDataBlockLength() {
        return writeDataBlockLength;
    }

    /**
     * @param writeDataBlockLength
     *            the writeDataBlockLength to set
     */
    public void setWriteDataBlockLength(int writeDataBlockLength) {
        this.writeDataBlockLength = writeDataBlockLength;
    }

    /**
     * @return the isRunningLLRPEncoding
     */
    public boolean isRunningLLRPEncoding() {
        return isRunningLLRPEncoding;
    }

    /**
     * @param isRunningLLRPEncoding
     *            the isRunningLLRPEncoding to set
     */
    public void setRunningLLRPEncoding(boolean isRunningLLRPEncoding) {
        this.isRunningLLRPEncoding = isRunningLLRPEncoding;
    }

    /**
     * @return the targetEpc
     */
    public String getTargetEpc() {
        return targetEpc;
    }

    /**
     * @param targetEpc
     *            the targetEpc to set
     */
    public void setTargetEpc(String targetEpc) {
        this.targetEpc = targetEpc;
    }

    /**
     * @return the tagMask
     */
    public String getTagMask() {
        return tagMask;
    }

    /**
     * @param tagMask
     *            the tagMask to set
     */
    public void setTagMask(String tagMask) {
        this.tagMask = tagMask;
    }

    /**
     * @return the operationsTimeout
     */
    public Integer getOperationsTimeout() {
        return operationsTimeout;
    }

    /**
     * @param operationsTimeout
     *            the operationsTimeout to set
     */
    public void setOperationsTimeout(Integer operationsTimeout) {
        this.operationsTimeout = operationsTimeout;
    }

    /**
     * @return the accessPwd
     */
    public String getAccessPwd() {
        return accessPwd;
    }

    /**
     * @param accessPwd
     *            the accessPwd to set
     */
    public void setAccessPwd(String accessPwd) {
        this.accessPwd = accessPwd;
    }

    /**
     * @return the oldAccessPwd
     */
    public String getOldAccessPwd() {
        return oldAccessPwd;
    }

    /**
     * @param oldAccessPwd
     *            the oldAccessPwd to set
     */
    public void setOldAccessPwd(String oldAccessPwd) {
        this.oldAccessPwd = oldAccessPwd;
    }

    /**
     * @return the killPwd
     */
    public String getKillPwd() {
        return killPwd;
    }

    /**
     * @param killPwd
     *            the killPwd to set
     */
    public void setKillPwd(String killPwd) {
        this.killPwd = killPwd;
    }

    /**
     * @return the killPwdLockPrivilege
     */
    public int getKillPwdLockPrivilege() {
        return killPwdLockPrivilege;
    }

    /**
     * @param killPwdLockPrivilege
     *            the killPwdLockPrivilege to set
     */
    public void setKillPwdLockPrivilege(int killPwdLockPrivilege) {
        this.killPwdLockPrivilege = killPwdLockPrivilege;
    }

    /**
     * @return the accessPwdLockPrivilege
     */
    public int getAccessPwdLockPrivilege() {
        return accessPwdLockPrivilege;
    }

    /**
     * @param accessPwdLockPrivilege
     *            the accessPwdLock to set
     */
    public void setAccessPwdLockPrivilege(int accessPwdLockPrivilege) {
        this.accessPwdLockPrivilege = accessPwdLockPrivilege;
    }

    /**
     * @return the epcLockPrivilege
     */
    public int getEpcLockPrivilege() {
        return epcLockPrivilege;
    }

    /**
     * @param epcLockPrivilege
     *            the epcLockPrivilege to set
     */
    public void setEpcLockPrivilege(int epcLockPrivilege) {
        this.epcLockPrivilege = epcLockPrivilege;
    }

    /**
     * @return the userMemoryLockPrivilege
     */
    public int getUserMemoryLockPrivilege() {
        return userMemoryLockPrivilege;
    }

    /**
     * @param userMemoryLockPrivilege the userMemoryLockPrivilege to set
     */
    public void setUserMemoryLockPrivilege(int userMemoryLockPrivilege) {
        this.userMemoryLockPrivilege = userMemoryLockPrivilege;
    }

    /**
     * 
     * @param sensor
     * @param id
     * @param host
     * @param port
     * @param reconnectionInterval
     * @param maxConAttempts
     * @param readerConfigPath
     * @param timeout
     * @param notifierService
     * @param readerID
     * @param commands
     */
    public LLRPReaderSession(AbstractSensor<?> sensor, String id, String host, int port, int reconnectionInterval,
            int maxConAttempts, String readerConfigPath, String rssiFilter, NotifierService notifierService,
            String readerID, Set<AbstractCommandConfiguration<?>> commands) {
        super(sensor, id, commands);
        this.host = host;
        this.port = port;
        this.connection = new LLRPConnector(this, host, port);
        this.maxConAttempts = maxConAttempts;
        this.reconnectionInterval = reconnectionInterval;
        this.readerConfigPath = readerConfigPath;
        this.notifierService = notifierService;
        this.readerID = readerID;
        this.setStatus(SessionStatus.CLOSED);
        this.setRSSIFilterMap(rssiFilter);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rifidi.edge.core.readers.ReaderSession#connect()
     */
    @Override
    protected synchronized void _connect() throws IOException {
        logger.info("LLRP Session " + this.getID() + " on sensor " + this.getSensor().getID()
                + " attempting to connect to " + host + ":" + port);
        this.setStatus(SessionStatus.CONNECTING);
        // Connected flag
        boolean connected = false;
        // try to connect up to MaxConAttempts number of times, unless
        // maxConAttempts is -1, in which case, try forever
        connectingLoop.set(true);
        try {
            for (int connCount = 0; connCount < maxConAttempts || maxConAttempts == -1; connCount++) {

                // attempt to make the connection
                try {
                    ((LLRPConnector) connection).connect();
                    connected = true;
                    break;
                } catch (LLRPConnectionAttemptFailedException e) {
                    // fix for abandoned connection
                    try {
                        ((LLRPConnector) connection).disconnect();
                    } catch (Exception ex) {
                    }
                    logger.debug("Attempt to connect to LLRP reader failed: " + connCount);
                } catch (RuntimeIOException e) {
                    // fix for abandoned connection
                    try {
                        ((LLRPConnector) connection).disconnect();
                    } catch (Exception ex) {
                    }
                    logger.debug("Attempt to connect to LLRP reader failed: " + connCount);
                }

                // wait for a specified number of ms or until someone calls
                // notify on the connetingLoop object
                try {
                    synchronized (connectingLoop) {
                        connectingLoop.wait(reconnectionInterval);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }

                // if someone else wants us to stop, break from the for loop
                if (!connectingLoop.get())
                    break;
            }
        } finally {
            // make sure connecting loop is false!
            connectingLoop.set(false);
        }

        // if not connected, exit
        if (!connected) {
            setStatus(SessionStatus.CLOSED);
            throw new IOException("Cannot connect");
        }

        // physical connection established, set up session
        timingOut.set(false);
        onConnect();

        logger.info("LLRP Session " + this.getID() + " on sensor " + this.getSensor().getID() + " connected to "
                + host + ":" + port);
    }

    /**
     * This logic executes as soon as a socket is established to initialize the
     * connection. It occurs before any commands are scheduled
     */
    private void onConnect() {
        logger.info("LLRP Session " + this.getID() + " on sensor " + this.getSensor().getID()
                + " attempting to log in to " + host + ":" + port);
        setStatus(SessionStatus.LOGGINGIN);
        executor = new ScheduledThreadPoolExecutor(1);

        try {
            SET_READER_CONFIG config = createSetReaderConfig();
            config.setMessageID(new UnsignedInteger(messageID++));

            SET_READER_CONFIG_RESPONSE config_response = (SET_READER_CONFIG_RESPONSE) connection.transact(config);

            StatusCode sc = config_response.getLLRPStatus().getStatusCode();
            if (sc.intValue() != StatusCode.M_Success) {
                if (config_response.getLLRPStatus().getStatusCode().toInteger() != 0) {
                    try {
                        logger.error("Problem with SET_READER_CONFIG: \n" + config_response.toXMLString());
                    } catch (InvalidLLRPMessageException e) {
                        logger.warn("Cannot print XML for " + "SET_READER_CONFIG_RESPONSE");
                    }
                }
            }

            // BytesToEnd_HEX data = new BytesToEnd_HEX();
            // CUSTOM_MESSAGE msg = new CUSTOM_MESSAGE();
            // msg.setVendorIdentifier(new UnsignedInteger(25882));
            // msg.setMessageSubtype(new UnsignedByte(21));
            // data.add(new SignedByte(0));
            // data.add(new SignedByte(0));
            // data.add(new SignedByte(0));
            // data.add(new SignedByte(0));
            // msg.setData(data);
            // connection.transact(msg);

            if (!processing.compareAndSet(false, true)) {
                logger.warn("Executor was already active! ");
            }
            this.lastTagTimestamp = System.currentTimeMillis();
            submit(getTimeoutCommand(this), 10, TimeUnit.SECONDS);
            setStatus(SessionStatus.PROCESSING);

        } catch (TimeoutException e) {
            logger.error(e.getMessage());
            disconnect();
        } catch (ClassCastException ex) {
            logger.error(ex.getMessage());
            disconnect();
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rifidi.edge.sensors.SensorSession#getResetCommand()
     */
    @Override
    protected Command getResetCommand() {
        return new LLRPReset("LLRPResetCommand");
    }

    /**
     * This method returns a command used to ping the reader. It is used to
     * determine if the connection is still alive
     * 
     * @return
     */
    private Command getTimeoutCommand(final LLRPReaderSession session) {
        return new TimeoutCommand("LLRP Timeout") {

            @Override
            protected void execute() throws TimeoutException {
                // If the last tag was seen less than 10 seconds ago, do
                // nothing.
                Long tenSecondsAgo = System.currentTimeMillis() - 10000;
                if (tenSecondsAgo > session.lastTagTimestamp) {
                    GET_READER_CAPABILITIES grc = new GET_READER_CAPABILITIES();
                    GetReaderCapabilitiesRequestedData data = new GetReaderCapabilitiesRequestedData();
                    data.set(GetReaderCapabilitiesRequestedData.LLRP_Capabilities);
                    grc.setRequestedData(data);
                    transact(grc);
                }
            }
        };
    }

    /**
     * 
     */
    @Override
    public void disconnect() {
        if (processing.get()) {
            try {
                resetCommands();
                this.submitAndBlock(getResetCommand(), getTimeout(), TimeUnit.MILLISECONDS);
                // if already connected, disconnect
                if (processing.compareAndSet(true, false)) {
                    logger.debug("Disconnecting");
                    ((LLRPConnector) connection).disconnect();
                }
            } catch (Exception e) {
                logger.error(e.getMessage());
            } finally {
                this.timingOut.set(false);
                // make sure executor is shutdown!
                if (executor != null) {
                    executor.shutdownNow();
                    executor = null;
                }
                // notify anyone who cares that session is now closed
                setStatus(SessionStatus.CLOSED);
            }
        } else {
            try {
                // Try to stop connecting
                if (connectingLoop.getAndSet(false)) {
                    synchronized (connectingLoop) {
                        connectingLoop.notify();
                    }
                }
            } finally {
                // make sure executor is shutdown!
                if (executor != null) {
                    executor.shutdownNow();
                    executor = null;
                }
                // notify anyone who cares that session is now closed
                setStatus(SessionStatus.CLOSED);
            }
        }
    }

    /**
     * This method sends a message and waits for an LLRP response message.
     * 
     * @param message
     *            The LLRP message to send to the reader
     * @return The response message
     * @throws TimeoutException
     *             If there was a timeout when processing this command.
     */
    public LLRPMessage transact(LLRPMessage message) throws TimeoutException {
        synchronized (connection) {
            try {
                if (timingOut.get()) {
                    throw new IllegalStateException("Cannot execute while timing out: " + message.getName());
                }
                LLRPMessage response = this.connection.transact(message, 20000);
                // Determine if the response has an error
                //            try {
                LLRPExceptionHandler handler = new LLRPExceptionHandler(this.readerID, this.sensor);
                handler.checkForErrors(response);

                //            } catch (InvalidLLRPMessageException e) {
                //               e.printStackTrace();
                //            }
                return response;
            } catch (TimeoutException e) {
                timingOut.set(true);
                logger.error("Timeout when sending an LLRP Message: " + message.getName());
                // Disconnect and possibly reconnect
                logger.info("Attempting to reconnect to reader " + this.readerID + " after a timeout");
                this.disconnect();
                throw e;
            }
        }
    }

    /**
     * This command sends a message and does not wait for a response. Transact
     * should normally be preferred to this method.
     * 
     * @param message
     */
    public void send(LLRPMessage message) {
        synchronized (connection) {
            connection.send(message);
        }
    }

    /**
     * 
     */
    @Override
    public void errorOccured(String arg0) {
        if (!arg0.contains("IllegalStateException")) {
            logger.error("LLRP Error Occurred: " + arg0);
        }
        // TODO: should we disconnect?
        //      try {
        //         this.disconnect();
        //      } catch (Exception e) {
        //         logger.error("Exception occurred trying to disconnect from reader " + this.readerID);
        //      }
        //      final LLRPReaderSession self = this;
        //      final String finalReaderID = this.readerID;
        //      Timer connectTimer = new Timer();
        //      connectTimer.schedule(new TimerTask() {         
        //         @Override
        //         public void run() {
        //            try {
        //               //Disconnect and possibly reconnect
        //               logger.info("Attempting to reconnect to reader " + finalReaderID + " after a timeout");
        //               self._connect();
        //            } catch (IOException e) {
        //               logger.error("Unable to reconnect to reader " + finalReaderID);
        //            }
        //         }
        //      }, 500);
    }

    /**
     * This method creates a SET_READER_CONFIG method.
     * 
     * @return The SET_READER_CONFIG object.
     */
    public SET_READER_CONFIG createSetReaderConfig() {
        try {
            String directory = System.getProperty("org.rifidi.home");
            String file = directory + File.separator + readerConfigPath;
            LLRPMessage message = Util.loadXMLLLRPMessage(new File(file));
            return (SET_READER_CONFIG) message;
        } catch (Exception e) {
            logger.warn("No SET_READER_CONFIG message found at " + readerConfigPath
                    + " Using default SET_READER_CONFIG");
        }

        return LLRPConstants.createDefaultConfig();
    }

    /**
     * Gets the reader config and returns the xml representation.  
     * 
     * @return
     * @throws TimeoutException 
     * @throws InvalidLLRPMessageException 
     */
    public String getReaderConfig() throws InvalidLLRPMessageException, TimeoutException {
        GET_READER_CONFIG grc = new GET_READER_CONFIG();
        grc.setAntennaID(new UnsignedShort(0));
        GetReaderConfigRequestedData data = new GetReaderConfigRequestedData();
        data.set(0);
        grc.setRequestedData(data);
        grc.setGPIPortNum(new UnsignedShort(0));
        grc.setGPOPortNum(new UnsignedShort(0));
        String retVal = this.transact(grc).toXMLString();
        return retVal;
    }

    /**
     * Gets the reader config and returns the xml representation.  
     * 
     * @return
     * @throws TimeoutException 
     * @throws InvalidLLRPMessageException 
     */
    public String getReaderCapabilities() throws InvalidLLRPMessageException, TimeoutException {
        GET_READER_CAPABILITIES grc = new GET_READER_CAPABILITIES();
        GetReaderCapabilitiesRequestedData data = new GetReaderCapabilitiesRequestedData();
        data.set(0);
        grc.setRequestedData(data);
        String retVal = this.transact(grc).toXMLString();
        return retVal;
    }

    /**
     * 
     * 
     * @param rospecID
     * @return
     * @throws TimeoutException 
     * @throws InvalidLLRPMessageException 
     */
    public String getRospecs() throws InvalidLLRPMessageException, TimeoutException {
        GET_ROSPECS gr = new GET_ROSPECS();
        String retVal = this.transact(gr).toXMLString();
        return retVal;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.rifidi.edge.core.readers.impl.AbstractReaderSession#setStatus(org
     * .rifidi.edge.core.api.SessionStatus)
     */
    @Override
    protected synchronized void setStatus(SessionStatus status) {
        super.setStatus(status);
        // TODO: Remove this once we have aspectJ
        NotifierService service = notifierService;
        if (service != null) {
            service.sessionStatusChanged(this.readerID, this.getID(), status);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rifidi.edge.sensors.base.AbstractSensorSession#submit(java.lang
     * .String, long, java.util.concurrent.TimeUnit)
     */
    @Override
    public Integer submit(String commandID, long interval, TimeUnit unit) {
        Integer retVal = super.submit(commandID, interval, unit);
        // TODO: Remove this once we have aspectJ
        try {
            NotifierService service = notifierService;
            if (service != null) {
                service.jobSubmitted(this.readerID, this.getID(), retVal, commandID, (interval > 0));
            }
        } catch (Exception e) {
            // make sure the notification doesn't cause this method to exit
            // under any circumstances
            logger.error(e);
        }
        return retVal;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.rifidi.edge.core.readers.impl.AbstractReaderSession#killComand(java
     * .lang.Integer)
     */
    @Override
    public void killComand(Integer id) {
        super.killComand(id);
        // TODO: Remove this once we have aspectJ
        NotifierService service = notifierService;
        if (service != null) {
            service.jobDeleted(this.readerID, this.getID(), id);
        }
    }

    private ADD_ACCESSSPEC_RESPONSE executeLLRPOperation(int accessSpecId, String epcId)
            throws InvalidLLRPMessageException, TimeoutException {

        // try {
        logger.info("Add accessspec! " + accessSpecId);
        // //// int accessSpecId = 2;
        AccessSpec spec = buildAccessSpec(accessSpecId, epcId);
        ADD_ACCESSSPEC aas = new ADD_ACCESSSPEC();
        aas.setAccessSpec(spec);
        logger.info(aas.toXMLString());
        ADD_ACCESSSPEC_RESPONSE response = null;

        response = (ADD_ACCESSSPEC_RESPONSE) this.transact(aas);
        logger.info("Response__: " + response.toXMLString());

        ENABLE_ACCESSSPEC enablespec = new ENABLE_ACCESSSPEC();
        enablespec.setAccessSpecID(new UnsignedInteger(accessSpecId));

        this.send(enablespec);

        return response;
        /*
         * } catch (Exception e) { e.printStackTrace(); return e.getMessage(); }
         */
    }

    // Execute only this operation:
    public LLRPEncodeMessageDto llrpWriteEpcOperation(String epc)
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPEPCWrite.name(),
                WRITE_EPC_ACCESSSPEC_ID, WRITE_EPC_OPSPEC_ID, epc);
        llrpOperationTracker.addOperationDto(epcWriteOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    // Execute only this operation EPC Read:
    //public LLRPEncodeMessageDto llrpReadEpcOperation(String epc)
    public LLRPEncodeMessageDto llrpReadEpcOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcReadOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPEPCRead.name(),
                READ_EPC_ACCESSSPEC_ID, READ_EPC_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcReadOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    // TODO to execute only this operation:
    public LLRPEncodeMessageDto llrpWriteAccessPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPAccessPasswordWrite.name(),
                WRITE_ACCESSPASS_ACCESSSPEC_ID, WRITE_ACCESSPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcWriteOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpReadAccessPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto accessPwdReadDto = new LLRPOperationDto(
                LLRP_OPERATION_CODE.LLRPAccessPasswordValidate.name(), READ_ACCESSPASS_ACCESSSPEC_ID,
                READ_ACCESSPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(accessPwdReadDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpReadKillPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto killPwdReadDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPKillPasswordRead.name(),
                READ_KILLPASS_ACCESSSPEC_ID, READ_KILLPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(killPwdReadDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpWriteKillPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPKillPasswordWrite.name(),
                WRITE_KILLPASS_ACCESSSPEC_ID, WRITE_KILLPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcWriteOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpReadUserMemoryOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcReadOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPUserMemoryRead.name(),
                READ_USER_MEMORY_ACCESSSPEC_ID, READ_USER_MEMORY_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcReadOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpReadMemoryBankOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcReadOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPMemoryBankRead.name(),
                READ_MEMORY_BANK_ACCESSSPEC_ID, READ_MEMORY_BANK_OPSEC_ID, null);
        llrpOperationTracker.addOperationDto(epcReadOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpWriteUserMemoryOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPUserMemoryWrite.name(),
                WRITE_USER_MEMORY_ACCESSSPEC_ID, WRITE_USER_MEMORY_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcWriteOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpLockEpcOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPEPCLock.name(),
                LOCK_EPC_ACCESSSPEC_ID, LOCK_EPC_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcLockOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpLockAccessPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPAccessPasswordLock.name(),
                LOCK_ACCESSPASS_ACCESSSPEC_ID, LOCK_ACCESSPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcLockOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpLockKillPasswordOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto epcLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPKillPasswordLock.name(),
                LOCK_KILLPASS_ACCESSSPEC_ID, LOCK_KILLPASS_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(epcLockOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    public LLRPEncodeMessageDto llrpLockUserMemoryOperation()
            throws InvalidLLRPMessageException, TimeoutException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto userMemoryLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPUserMemoryLock.name(),
                LOCK_USER_MEMORY_ACCESSSPEC_ID, LOCK_USER_MEMORY_OPSPEC_ID, null);
        llrpOperationTracker.addOperationDto(userMemoryLockOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    // public String addAccessSpec(String writeAccessPassword, String writeData
    // ) {
    public LLRPEncodeMessageDto llrpEncode(String strTag)
            throws TimeoutException, InvalidLLRPMessageException, Exception {

        // Initialize operation tracker, to be able to receive asynchronous
        // messages
        llrpOperationTracker = new LLRPOperationTracker(this);

        // Add the operations in the same order we want to be executed by this
        // tracker

        LLRPOperationDto accessPwdWriteOpDto = new LLRPOperationDto(
                LLRP_OPERATION_CODE.LLRPAccessPasswordWrite.name(), WRITE_ACCESSPASS_ACCESSSPEC_ID,
                WRITE_ACCESSPASS_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(accessPwdWriteOpDto);

        LLRPOperationDto epcWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPEPCWrite.name(),
                WRITE_EPC_ACCESSSPEC_ID, WRITE_EPC_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(epcWriteOpDto);

        LLRPOperationDto killPwdWriteOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPKillPasswordWrite.name(),
                WRITE_KILLPASS_ACCESSSPEC_ID, WRITE_KILLPASS_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(killPwdWriteOpDto);

        LLRPOperationDto epcLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPEPCLock.name(),
                LOCK_EPC_ACCESSSPEC_ID, LOCK_EPC_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(epcLockOpDto);

        LLRPOperationDto killPwdLockOpDto = new LLRPOperationDto(LLRP_OPERATION_CODE.LLRPKillPasswordLock.name(),
                LOCK_KILLPASS_ACCESSSPEC_ID, LOCK_KILLPASS_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(killPwdLockOpDto);

        LLRPOperationDto accessPwdLockOpDto = new LLRPOperationDto(
                LLRP_OPERATION_CODE.LLRPAccessPasswordLock.name(), LOCK_ACCESSPASS_ACCESSSPEC_ID,
                LOCK_ACCESSPASS_OPSPEC_ID, strTag);
        llrpOperationTracker.addOperationDto(accessPwdLockOpDto);

        return startRequestedOperations(llrpOperationTracker);

    }

    /**
     * 
     * @param llrpOperationTracker
     * @param isSynchronous
     *            if operations are going to be executed in synchronous mode,
     *            otherwise they execute in asynchronous mode
     * @throws TimeoutException
     * @throws InvalidLLRPMessageException
     */
    private LLRPEncodeMessageDto startRequestedOperations(LLRPOperationTracker llrpOperationTracker)
            throws TimeoutException, InvalidLLRPMessageException, Exception {

        // Start the requested operations
        setRunningLLRPEncoding(true);

        // Variable to return asynchronous response
        LLRPEncodeMessageDto llrpEncodeMessageDto = new LLRPEncodeMessageDto();

        // Call the operations in tracker
        for (LLRPOperationDto llrpOperationDto : llrpOperationTracker.getOperationList()) {

            ADD_ACCESSSPEC_RESPONSE response = executeLLRPOperation(llrpOperationDto.getAccessSpecId(),
                    llrpOperationDto.getEpc());

            llrpOperationDto.setAccessSpecResponse(response);

            // Check if response is failure, and return synchronous message
            if (response.getLLRPStatus().getStatusCode().intValue() != StatusCode.M_Success) {
                throw new Exception(response.toXMLString());
            }

        }

        if (isExecuteOperationsInAsynchronousMode()) {

            llrpOperationTracker.initializeMqttParameters();

            // Check the operation status in asynchronous mode
            // Add a timer to control
            Timer timer = new Timer(true);
            timer.schedule(llrpOperationTracker, new Date());
            logger.info("TimerTask begins! :" + new Date());

            //TODO change Success hard coded value
            llrpEncodeMessageDto.setStatus("Success");

        } else {

            // Check the operation status in synchronous mode
            llrpEncodeMessageDto = llrpOperationTracker.checkOperationStatus();

        }

        return llrpEncodeMessageDto;
    }

    /**
     * Split the hex array into groups of 4 by splitting on a colon charater.
     * 
     * @param arg
     * @return
     */
    public String[] splitHexArray(String arg) {
        ArrayList<String> retVal = new ArrayList<String>();
        for (int i = 0; i < arg.length(); i += 4) {
            retVal.add(arg.substring(i, i + 4));
        }
        return retVal.toArray(new String[arg.length()]);
    }

    public String sendLLRPMessage(Document xmlMessage, boolean sendonly) {
        try {
            LLRPMessage message = LLRPMessageFactory.createLLRPMessage(xmlMessage);
            LLRPMessage response = null;
            try {
                if (!sendonly) {
                    response = this.transact(message);
                    return response.toXMLString();
                } else {
                    this.send(message);
                    return null;
                }
            } catch (TimeoutException e) {
                return e.getMessage();
            }
        } catch (Exception e) {
            return e.getMessage();
        }
    }

    /**
     * This method receives asynchronous messages back from the LLRP reader. We
     * add relavent events to the esper runtime
     */
    @Override
    public void messageReceived(LLRPMessage arg0) {
        LLRPExceptionHandler handler = new LLRPExceptionHandler(this.readerID, this.sensor);
        try {
            handler.checkForErrors(arg0);
            if (logger.isDebugEnabled()) {
                logger.debug(arg0.getResponseType());
                logger.debug(arg0.toXMLString());
            }
        } catch (InvalidLLRPMessageException e) {
            e.printStackTrace();
        }
        if (arg0.getTypeNum() == RO_ACCESS_REPORT.TYPENUM) {
            this.lastTagTimestamp = System.currentTimeMillis();
            // The message received is an Access Report.
            RO_ACCESS_REPORT report = (RO_ACCESS_REPORT) arg0;
            // Get a list of the tags read.
            List<TagReportData> tags = report.getTagReportDataList();
            // Loop through the list and get the EPC of each tag.
            for (TagReportData tag : tags) {
                // System.out.println(tag.getEPCParameter());
                // System.out.println(tag.getLastSeenTimestampUTC());
                List<AccessCommandOpSpecResult> ops = tag.getAccessCommandOpSpecResultList();
                // See if any operations were performed on
                // this tag (read, write, kill).
                // If so, print out the details.

                if (this.numTagsChecker) {
                    if (this.numTagsSet != null) {
                        if (tag.getEPCParameter() instanceof EPC_96) {
                            EPC_96 id = (EPC_96) tag.getEPCParameter();
                            this.numTagsSet.add(id.getEPC().toString());
                        } else {
                            EPCData id = (EPCData) tag.getEPCParameter();
                            this.numTagsSet.add(id.getEPC().toString());
                        }
                    }
                }

                for (AccessCommandOpSpecResult op : ops) {

                    // Assign the result to appropriate operation in tracker
                    llrpOperationTracker.setResult(op);

                    logger.info(op.toString());

                }

                // Delete access spec if there is a CommandOpSpecResult
                // coming back.
                //this.deleteAccessSpecs();
            }
        }

        if (!processing.get()) {
            return;
        }
        try {
            Object event = LLRPEventFactory.createEvent(arg0, readerID, rssiFilterMap);
            if (event != null) {
                if (event instanceof ReadCycle) {
                    ReadCycle cycle = (ReadCycle) event;
                    sensor.send(cycle);

                    // TODO: get rid of this for performance reasons. Need to
                    // have a better way to figure out if we need to send tag
                    // read to JMS
                    // this.getTemplate().send(this.getDestination(),
                    // new ObjectMessageCreator(cycle));
                } else {
                    sensor.sendEvent(event);
                }
            }

        } catch (Exception e) {
            logger.error("Exception while parsing message: " + e.getMessage());
        }

        /*
         * // Timeout check: long millisToSleep = 500; int numberOfLoops = (int)
         * (getTimeout() / millisToSleep);
         * 
         * for (int i = 0; i < numberOfLoops; i++) {
         * 
         * // Check if all messages received, if so and all succeed return //
         * success if (llrpOperationTracker.areAllResultsReceived()) {
         * 
         * break;
         * 
         * } else {
         * 
         * try {
         * 
         * // Sleep the time out Thread.sleep(millisToSleep);
         * 
         * } catch (InterruptedException e) {
         * 
         * // No matters } }
         * 
         * }
         * 
         * if (llrpOperationTracker.areAllResultsSuccessful()) {
         * 
         * // TODO post to queue a single success message
         * System.out.println("allResultsSuccessful()");
         * 
         * } else {
         * 
         * System.out.println("NOT allResultsSuccessful()"); // TODO post to
         * queue a fail message }
         * 
         * //Free session
         */
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "LLRPSession: " + host + ":" + port + " (" + getStatus() + ")";
    }

    private boolean numTagsChecker = false;
    private HashSet<String> numTagsSet;

    public Integer numTagsOnLLRP() {
        this.numTagsChecker = true;
        numTagsSet = new HashSet<String>();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.numTagsChecker = false;
        return numTagsSet.size();
    }

    /*
     * public enum LLRPWriteOperationEnum { WRITE_EPC, WRITE_ACCESSPASS,
     * WRITE_KILLPASS, LOCK_EPC, LOCK_KILL_PASSWORD, LOCK_ACCESS_PASSWORD }
     */

    // The TAG_MASK and TARGET_EPC constants are used to define which tags we
    // want to write to. The reader will take each tag EPC and bitwise AND it
    // with the TAG_MASK. If the result matches the value specified in the
    // TARGET_EPC constant, the reader will write to that tag. The other
    // constants defined above are used for message IDs. They can be any unique
    // integer value. Next, we'll add the functions required to read and write
    // user memory.

    // Enable the AccessSpec
    public void enableAccessSpec(int accessSpecID) {
        logger.info("Enabling the AccessSpec.");
        ENABLE_ACCESSSPEC enable = new ENABLE_ACCESSSPEC();
        enable.setAccessSpecID(new UnsignedInteger(accessSpecID));
        try {
            // response = (ENABLE_ACCESSSPEC_RESPONSE) this.transact(enable,
            // TIMEOUT_MS);
            // System.out.println(response.toXMLString());
        } catch (Exception e) {
            logger.info("Error enabling AccessSpec.");
            e.printStackTrace();
        }
    }

    // Delete all AccessSpecs from the reader
    public void deleteAccessSpecs() {

        logger.info("Deleting all AccessSpecs.");
        DELETE_ACCESSSPEC del = new DELETE_ACCESSSPEC();
        // Use zero as the ROSpec ID.
        // This means delete all AccessSpecs.
        del.setAccessSpecID(new UnsignedInteger(0));
        try {
            // response = (DELETE_ACCESSSPEC_RESPONSE) reader.transact(del,
            // TIMEOUT_MS);
            // System.out.println(response.toXMLString());
        } catch (Exception e) {
            logger.info("Error deleting AccessSpec.");
            e.printStackTrace();
        }
    }

    /*
     * // Create a OpSpec that reads from user memory public C1G2Read
     * buildReadOpSpec() { // Create a new OpSpec. // This specifies what
     * operation we want to perform on the // tags that match the specifications
     * above. // In this case, we want to read from the tag. C1G2Read opSpec =
     * new C1G2Read(); // Set the OpSpecID to a unique number.
     * opSpec.setOpSpecID(new UnsignedShort(READ_OPSPEC_ID));
     * opSpec.setAccessPassword(new UnsignedInteger(0)); // For this demo, we'll
     * read from user memory (bank 3). TwoBitField opMemBank = new
     * TwoBitField(); // Set bits 0 and 1 (bank 3 in binary). opMemBank.set(0);
     * opMemBank.set(1); opSpec.setMB(opMemBank); // We'll read from the base of
     * this memory bank (0x00). opSpec.setWordPointer(new UnsignedShort(0x00));
     * // Read two words. opSpec.setWordCount(new UnsignedShort(2));
     * 
     * return opSpec; }
     */

    // Create an AccessSpec.
    // It will contain our two OpSpecs (read and write).
    public AccessSpec buildAccessSpec(int accessSpecID, String epcId) {
        // LLRPWriteOperationEnum writeOperation) {

        logger.info("Building the AccessSpec.");

        AccessSpec accessSpec = new AccessSpec();

        accessSpec.setAccessSpecID(new UnsignedInteger(accessSpecID));

        // Set ROSpec ID to zero.
        // This means that the AccessSpec will apply to all ROSpecs.
        accessSpec.setROSpecID(new UnsignedInteger(0));
        // Antenna ID of zero means all antennas.
        accessSpec.setAntennaID(new UnsignedShort(0));
        accessSpec.setProtocolID(new AirProtocols(AirProtocols.EPCGlobalClass1Gen2));
        // AccessSpecs must be disabled when you add them.
        accessSpec.setCurrentState(new AccessSpecState(AccessSpecState.Disabled));
        AccessSpecStopTrigger stopTrigger = new AccessSpecStopTrigger();
        // Stop after the operating has been performed a certain number of
        // times.
        // That number is specified by the Operation_Count parameter.
        stopTrigger
                .setAccessSpecStopTrigger(new AccessSpecStopTriggerType(AccessSpecStopTriggerType.Operation_Count));
        // OperationCountValue indicate the number of times this Spec is
        // executed before it is deleted. If set to 0, this is equivalent
        // to no stop trigger defined.
        stopTrigger.setOperationCountValue(new UnsignedShort(1));
        accessSpec.setAccessSpecStopTrigger(stopTrigger);

        // Create a new AccessCommand.
        // We use this to specify which tags we want to operate on.
        AccessCommand accessCommand = new AccessCommand();

        // Create a new tag spec.
        C1G2TagSpec tagSpec = new C1G2TagSpec();
        C1G2TargetTag targetTag = new C1G2TargetTag();
        targetTag.setMatch(new Bit(1));
        // We want to check memory bank 1 (the EPC memory bank).
        TwoBitField memBank = new TwoBitField();
        // Clear bit 0 and set bit 1 (bank 1 in binary).

        memBank.clear(0);
        memBank.set(1);

        targetTag.setMB(memBank);
        // The EPC data starts at offset 0x20.
        // Start reading or writing from there.
        targetTag.setPointer(new UnsignedShort(0x20));
        // This is the mask we'll use to compare the EPC.
        // We want to match all bits of the EPC, so all mask bits are set.
        BitArray_HEX hexTagMask = new BitArray_HEX(getTagMask());
        targetTag.setTagMask(hexTagMask);
        // We only only to operate on tags with this EPC.
        BitArray_HEX hexTagData = new BitArray_HEX(getTargetEpc());
        targetTag.setTagData(hexTagData);

        // Add a list of target tags to the tag spec.
        List<C1G2TargetTag> targetTagList = new ArrayList<C1G2TargetTag>();
        targetTagList.add(targetTag);
        tagSpec.setC1G2TargetTagList(targetTagList);

        // Add the tag spec to the access command.
        accessCommand.setAirProtocolTagSpec(tagSpec);

        // A list to hold the op specs for this access command.
        List<AccessCommandOpSpec> opSpecList = new ArrayList<AccessCommandOpSpec>();

        if (accessSpecID == WRITE_EPC_ACCESSSPEC_ID) {

            opSpecList.add(buildEpcWriteOpSpec(epcId));

        } else if (accessSpecID == WRITE_KILLPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildEpcWriteKillPass());

        } else if (accessSpecID == WRITE_ACCESSPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildEpcWriteAccessPass());

        } else if (accessSpecID == LOCK_ACCESSPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildLockOpSpec(LOCK_ACCESSPASS_OPSPEC_ID, getAccessPwdLockPrivilege(),
                    C1G2LockDataField.Access_Password));

        } else if (accessSpecID == LOCK_KILLPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildLockOpSpec(LOCK_KILLPASS_OPSPEC_ID, getKillPwdLockPrivilege(),
                    C1G2LockDataField.Kill_Password));

        } else if (accessSpecID == LOCK_EPC_ACCESSSPEC_ID) {

            opSpecList
                    .add(buildLockOpSpec(LOCK_EPC_OPSPEC_ID, getEpcLockPrivilege(), C1G2LockDataField.EPC_Memory));

        } else if (accessSpecID == READ_EPC_ACCESSSPEC_ID) {

            opSpecList.add(buildEpcReadOpSpec());

        } else if (accessSpecID == READ_KILLPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildKillPassRead());

        } else if (accessSpecID == READ_ACCESSPASS_ACCESSSPEC_ID) {

            opSpecList.add(buildAccessPassRead());

        } else if (accessSpecID == READ_USER_MEMORY_ACCESSSPEC_ID) {

            opSpecList.add(buildUserMemoryReadOpSpec());

        } else if (accessSpecID == READ_MEMORY_BANK_ACCESSSPEC_ID) {
            opSpecList.add(buildMemBankReadOpSpec());
        } else if (accessSpecID == WRITE_USER_MEMORY_ACCESSSPEC_ID) {

            opSpecList.add(buildUserMemoryWriteOpSpec());

        } else if (accessSpecID == LOCK_USER_MEMORY_ACCESSSPEC_ID) {

            opSpecList.add(buildLockOpSpec(LOCK_USER_MEMORY_OPSPEC_ID, getUserMemoryLockPrivilege(),
                    C1G2LockDataField.User_Memory));

        }

        /*
         * else { // TODO remove this else statement, and add the other three
         * opSpecList.add(buildReadOpSpec()); }
         */

        accessCommand.setAccessCommandOpSpecList(opSpecList);

        // Add access command to access spec.
        accessSpec.setAccessCommand(accessCommand);

        // Add an AccessReportSpec.
        // We want to get notification when the operation occurs.
        // Tell the reader to sent it to us with the ROSpec.
        AccessReportSpec reportSpec = new AccessReportSpec();
        reportSpec.setAccessReportTrigger(
                new AccessReportTriggerType(AccessReportTriggerType.Whenever_ROReport_Is_Generated));

        return accessSpec;
    }

    // Create a OpSpec that writes to epc memory
    public C1G2BlockWrite buildEpcWriteOpSpec(String epcId) {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        // In this case, we want to write to the tag.
        C1G2BlockWrite opSpec = new C1G2BlockWrite();

        // Set the OpSpecID to a unique number.

        // opSpec.setOpSpecID(new UnsignedShort(WRITE_OPSPEC_ID));
        opSpec.setOpSpecID(WRITE_EPC_OPSPEC_ID);

        // opSpec.setAccessPassword(new UnsignedInteger(getAccessPwd()));
        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();

        // Set bit 1 (bank 2 in binary).
        opMemBank.set(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);

        opSpec.setWordPointer(new UnsignedShort(0x02));

        UnsignedShortArray_HEX writeArray = getFormatedWriteArrayData(epcId);

        opSpec.setWriteData(writeArray);

        return opSpec;
    }

    // Create a OpSpec that writes the access password
    // For a new tag, an old password is always zero
    public C1G2BlockWrite buildEpcWriteAccessPass() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        // In this case, we want to write to the tag.
        C1G2BlockWrite opSpec = new C1G2BlockWrite();
        // Set the OpSpecID to a unique number.
        opSpec.setOpSpecID(WRITE_ACCESSPASS_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getOldAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();
        // Clear twobits (bank 0 in binary).
        opMemBank.clear(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);
        opSpec.setWordPointer(new UnsignedShort(2));

        UnsignedShortArray_HEX writeArray = getFormatedWriteArrayData(getAccessPwd());

        opSpec.setWriteData(writeArray);

        return opSpec;
    }

    // Create a OpSpec that writes the kill password
    public C1G2BlockWrite buildEpcWriteKillPass() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        // In this case, we want to write to the tag.
        C1G2BlockWrite opSpec = new C1G2BlockWrite();
        // Set the OpSpecID to a unique number.
        opSpec.setOpSpecID(WRITE_KILLPASS_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();
        // Clear twobits (bank 0 in binary).
        opMemBank.clear(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);
        opSpec.setWordPointer(new UnsignedShort(0));

        UnsignedShortArray_HEX writeArray = getFormatedWriteArrayData(getKillPwd());

        opSpec.setWriteData(writeArray);

        return opSpec;
    }

    private UnsignedShortArray_HEX getFormatedWriteArrayData(String data) {

        UnsignedShortArray_HEX writeArray = new UnsignedShortArray_HEX();

        // It is required that the remainder of data.length() /
        // getWriteDataBlockLength() is zero
        if (data.length() % getWriteDataBlockLength() == 0) {

            int numberOfBlocks = data.length() / getWriteDataBlockLength();

            for (int i = 0; i < numberOfBlocks; i++) {

                int currentIndex = i * getWriteDataBlockLength();
                String word = data.substring(currentIndex, currentIndex + getWriteDataBlockLength());
                logger.info("Adding short: " + word);
                writeArray.add(new UnsignedShort(word, 16));

            }
        } else if (data.length() == 1) {

            // The data could be a value of zero
            writeArray.add(new UnsignedShort(data, 16));

        } else {

            throw new RuntimeException(
                    "The value " + data + " does not satisfy data.length() % getWriteDataBlockLength() == 0");

        }

        return writeArray;
    }

    /**
     * Converts string representation of lock privilege into int representation
     * 
     * @param lockPrivilege
     *            the string lock value to be parsed into int representation
     * @return int representation of this string lock value
     * @throws Exception
     *             if lockPrivilege is invalid, that is, is not any of
     *             Read_Write, Perma_Lock or Unlock string values
     */
    public static int getLockPrivilege(String lockPrivilege) throws Exception {

        Exception e = new Exception("Invalid lock privilege value: " + lockPrivilege + ". Valid ones are: "
                + LLRPReaderSession.READ_WRITE_LOCK_PRIVILEGE + ", " + LLRPReaderSession.PERMA_LOCK_PRIVILEGE + ", "
                + LLRPReaderSession.UNLOCK_PRIVILEGE);

        if (lockPrivilege == null || lockPrivilege.isEmpty()) {
            throw e;
        }

        if (lockPrivilege.equals(READ_WRITE_LOCK_PRIVILEGE)) {

            return C1G2LockPrivilege.Read_Write;

        } else if (lockPrivilege.equals(PERMA_LOCK_PRIVILEGE)) {

            return C1G2LockPrivilege.Perma_Lock;

        } else if (lockPrivilege.equals(UNLOCK_PRIVILEGE)) {

            return C1G2LockPrivilege.Unlock;

        } else {
            throw e;
        }

    }

    /**
     * 
     * @param opSpecId
     * @param lockPrivilege
     * @param lockDataField
     * @return
     */
    public C1G2Lock buildLockOpSpec(UnsignedShort opSpecId, int lockPrivilege, int lockDataField) {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        // In this case, we want to write to the tag.
        C1G2Lock opSpec = new C1G2Lock();

        // Set the OpSpecID to a unique number.

        opSpec.setOpSpecID(opSpecId);

        // opSpec.setWordPointer(new UnsignedShort(0x02));->only for writing

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);
        C1G2LockPayload payload = new C1G2LockPayload();
        C1G2LockDataField dataField = new C1G2LockDataField(lockDataField);
        payload.setDataField(dataField);

        C1G2LockPrivilege privilege = new C1G2LockPrivilege(lockPrivilege);
        payload.setPrivilege(privilege);

        List<C1G2LockPayload> c1G2LockPayloadList = new ArrayList<C1G2LockPayload>();
        c1G2LockPayloadList.add(payload);

        opSpec.setC1G2LockPayloadList(c1G2LockPayloadList);

        return opSpec;
    }

    /**
     * Cleans up the session and access specs
     */
    public void cleanupSession() {

        deleteAccessSpecs();
        setRunningLLRPEncoding(false);

    }

    // Create a OpSpec that reads the EPC
    public C1G2Read buildEpcReadOpSpec() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        C1G2Read opSpec = new C1G2Read();

        // Set the OpSpecID to a unique number.

        opSpec.setOpSpecID(READ_EPC_OPSPEC_ID);

        // opSpec.setAccessPassword(new UnsignedInteger(getAccessPwd()));
        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();

        // Set bit 1 (bank 2 in binary).
        opMemBank.set(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);

        opSpec.setWordPointer(new UnsignedShort(0x02));

        // Read n words. 
        opSpec.setWordCount(new UnsignedShort(getWordCount()));

        return opSpec;
    }

    // Create a OpSpec that reads the access password
    public C1G2Read buildAccessPassRead() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        C1G2Read opSpec = new C1G2Read();
        // Set the OpSpecID to a unique number.
        opSpec.setOpSpecID(READ_ACCESSPASS_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getOldAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();
        // Clear twobits (bank 0 in binary).
        opMemBank.clear(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);
        opSpec.setWordPointer(new UnsignedShort(2));

        // Read two words. 
        opSpec.setWordCount(new UnsignedShort(2));

        return opSpec;
    }

    // Create a OpSpec that reads the kill password
    public C1G2Read buildKillPassRead() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        // In this case, we want to write to the tag.
        C1G2Read opSpec = new C1G2Read();
        // Set the OpSpecID to a unique number.
        opSpec.setOpSpecID(READ_KILLPASS_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getOldAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();
        // Clear twobits (bank 0 in binary).
        opMemBank.clear(1);
        opMemBank.clear(0);

        opSpec.setMB(opMemBank);
        opSpec.setWordPointer(new UnsignedShort(0));

        //Read two words. 
        opSpec.setWordCount(new UnsignedShort(2));

        return opSpec;
    }

    // Create a OpSpec that reads from user memory public C1G2Read
    //public C1G2Read buildEpcReadOpSpec(String epcId) {
    public C1G2Read buildUserMemoryReadOpSpec() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        C1G2Read opSpec = new C1G2Read();

        // Set the OpSpecID to a unique number.

        opSpec.setOpSpecID(READ_USER_MEMORY_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();

        opMemBank.set(0);
        opMemBank.set(1);

        opSpec.setMB(opMemBank);

        opSpec.setWordPointer(new UnsignedShort(0x00));

        // Read 2 words. 
        opSpec.setWordCount(new UnsignedShort(getWordCount()));

        return opSpec;
    }

    // Create a OpSpec that reads from user memory public C1G2Read
    //public C1G2Read buildEpcReadOpSpec(String epcId) {
    public C1G2Read buildMemBankReadOpSpec() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        C1G2Read opSpec = new C1G2Read();

        // Set the OpSpecID to a unique number.

        opSpec.setOpSpecID(READ_MEMORY_BANK_OPSEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();

        //opMemBank.set(1);
        //opMemBank.set(0);
        if (this.membank1 == 1) {
            opMemBank.set(0);
        } else {
            opMemBank.clear(0);
        }
        if (this.membank2 == 1) {
            opMemBank.set(1);
        } else {
            opMemBank.clear(1);
        }

        opSpec.setMB(opMemBank);

        opSpec.setWordPointer(new UnsignedShort(0x00));

        // Read 2 words. 
        opSpec.setWordCount(new UnsignedShort(getWordCount()));

        return opSpec;
    }

    // Create a OpSpec that writes to user memory
    public C1G2Write buildUserMemoryWriteOpSpec() {
        // Create a new OpSpec.
        // This specifies what operation we want to perform on the
        // tags that match the specifications above.
        C1G2Write opSpec = new C1G2Write();

        // Set the OpSpecID to a unique number.

        opSpec.setOpSpecID(WRITE_USER_MEMORY_OPSPEC_ID);

        UnsignedInteger unsignedIntAccesPass = new UnsignedInteger(getAccessPwd(), 16);

        opSpec.setAccessPassword(unsignedIntAccesPass);

        // For this demo, we'll write to user memory (bank 3).
        TwoBitField opMemBank = new TwoBitField();

        // Set bit 1 (bank 2 in binary).
        opMemBank.set(0);
        opMemBank.set(1);

        opSpec.setMB(opMemBank);

        // We'll write to the base of this memory bank (0x00).
        opSpec.setWordPointer(new UnsignedShort(0x00));

        UnsignedShortArray_HEX writeData = getFormatedWriteArrayData(getUserMemoryData());

        // We'll write 8 bytes or two words.
        //writeData.add(new UnsignedShort (0xAABB));
        //writeData.add(new UnsignedShort (0xCCDD));
        opSpec.setWriteData(writeData);

        return opSpec;
    }

}