/// <summary>
        /// Send a Hart-IP Request
        /// </summary>
        /// <param name="Request"><see cref="HartIPRequest"></see></param>
        /// <para>see the HartIPRequest.Command for HART specification references.</para>
        /// <returns>bool true if the request is sent success.</returns>
        private bool SendHartIPRequest(HartIPRequest Request)
        {
            bool bSuccess = false;

            lock (SyncRoot)
            {
                m_Error = String.Empty;
                try
                {
                    bSuccess = SendRequest(Request);
                    LogMsg.Instance.Log(Request.ToString());
                }
                catch (SocketException se)
                {
                    m_Error = String.Format("SendHartIPRequest SocketException: ErrorCode:{0}. {1}",
                                            se.ErrorCode, se.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                }
                catch (Exception e)
                {
                    m_Error = String.Format("SendHartIPRequest Exception: {0}", e.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                }
            }

            return(bSuccess);
        }
 /// <summary>
 /// Contructor
 /// </summary>
 /// <param name="Req">HartIPRequest</param>
 /// <param name="nNumOfRetries">uint</param>
 /// <param name="nRetryDelay">uint</param>
 public RequestTask(HartIPRequest Req, uint nNumOfRetries,
                    uint nRetryDelay)
 {
     this.HartIPReq        = Req;
     this.HartIPRsp        = null;
     this.nNumberOfRetries = nNumOfRetries;
     this.nDrRetries       = 0;
     this.nRetryDelay      = nRetryDelay;
     this.bIsCompleted     = false;
 }
        /// <summary>
        /// Send HART-IP request
        /// </summary>
        /// <param name="Request"><see cref="HartIPRequest"/></param>
        /// <returns><see cref="HartIPResponse"/></returns>
        private HartIPResponse SendRequest(HartIPRequest Request)
        {
            HartIPResponse Rsp = null;

            do
            {
                try
                {
                    if (m_HartIPConn != null)
                    {
                        MsgResponse MsgRsp = new MsgResponse(Request.m_Timeout);
                        if (!m_HartIPConn.SendHartRequest(Request, MsgRsp))
                        {
                            m_Error = m_HartIPConn.LastError;
                            break;
                        }

                        // wait for the object return or timeout
                        if (MsgRsp.GetResponse())
                        {
                            Rsp = MsgRsp.ResponseMsg;
                        }
                    }

                    if (Rsp == null)
                    {
                        if ((m_HartIPConn != null) && (m_HartIPConn.LastError.Length > 0))
                        {
                            m_Error = m_HartIPConn.LastError;
                        }
                        else
                        {
                            m_Error = "Waiting Hart-IP Request waiting response timeout.";
                        }

                        Logger.Log(m_Error, false);
                        break;
                    }
                }
                catch (ThreadInterruptedException Ex)
                {
                    m_Error = "Send Hart Request Exception: " + Ex.Message;
                    Logger.Log(m_Error, true);
                }
                catch (ThreadStateException TSEx)
                {
                    m_Error = "Send Hart Request Exception: " + TSEx.Message;
                    Logger.Log(m_Error, true);
                }
            } while (false); /* ONCE */

            return(Rsp);
        }
        /// <summary>
        /// Send a Hart-IP Request
        /// </summary>
        /// <param name="Request"><see cref="HartIPRequest"></see></param>
        /// <param name="MsgRsp"><see cref="MsgResponse"></see></param>
        /// <para>see the HartIPRequest.Command for HART specification references.</para>
        /// <returns>bool if it is success</returns>
        public bool SendHartRequest(HartIPRequest Request, MsgResponse MsgRsp)
        {
            bool bSuccess = false;

            if (Request == null)
            {
                throw new ArgumentException("Invalid argument in SendHartRequest.");
            }

            lock (SyncRoot)
            {
                m_Error = String.Empty;
                try
                {
                    if (m_RspMsgReader == null)
                    {
                        // Create a thread that is constantly reading on the input stream
                        m_bStopped          = false;
                        m_RspMsgReader      = new Thread(new ThreadStart(this.ReceiveMsg));
                        m_RspMsgReader.Name = "HartIPConnection";
                        m_RspMsgReader.Start();
                    }

                    // add the request object into the m_Requests list
                    MsgRequest MsgReq = new MsgRequest(Request.TransactionId, MsgRsp);
                    m_Requests.Add(MsgReq);

                    // send the request
                    bSuccess = SendRequest(Request);
                    LogMsg.Instance.Log(Request.ToString());
                }
                catch (SocketException se)
                {
                    m_Error = String.Format("SendHartRequest SocketException: ErrorCode:{0}. {1}",
                                            se.ErrorCode, se.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                }
                catch (Exception e)
                {
                    m_Error = String.Format("SendHartRequest Exception: {0}", e.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                }

                if (!bSuccess)
                {
                    LogMsg.Instance.Log("Error, Failed sending Request: " + Request.ToString(), true);
                    DequeueRequest(Request.TransactionId);
                }
            }

            return(bSuccess);
        }
        /// <summary>
        /// Send a Hart request to connected network HART-IP device.
        /// </summary>
        /// <param name="Request"><see cref="HartIPRequest"/></param>
        /// <returns>bool True if it is success to send the HART request.</returns>
        protected override bool SendRequest(HartIPRequest Request)
        {
            bool   bSuccess  = false;
            String TimeStamp = HartUtil.GetTimeStamp();

            if (m_socket != null)
            {
                m_socket.Send(Request.Message);
                Request.TimeStamp = TimeStamp;
                bSuccess          = true;
            }
            else
            {
                m_Error = "SendRequest Error: Socket is already closed.";
                LogMsg.Instance.Log("Error, Socket is already closed in SendRequest.", true);
            }

            return(bSuccess);
        }
        /// <summary>
        /// Initiate a HART Session request
        /// </summary>
        /// <param name="usTranId">ushort Transaction Id</param>
        /// <param name="InactivityCloseTime">uint Session inactivity close time</param>
        /// <returns><see cref="HartIPRequest"> initiate session request</see></returns>
        public static HartIPRequest InitiateSession(ushort usTranId, uint InactivityCloseTime)
        {
            // create message header for this request
            HartIPRequest request = new HartIPRequest(MessageId.SESSION_INITIATE, usTranId);

            byte[] Params = new byte[5];
            Params[0] = HARTIPMessage.HART_SESSION_MASTER_TYPE;
            // swap the InactivityCloseTime bytes to 'host to network order'
            Params[1] = (byte)((InactivityCloseTime >> 24) & 0x0ff);
            Params[2] = (byte)((InactivityCloseTime >> 16) & 0x0ff);
            Params[3] = (byte)((InactivityCloseTime >> 8) & 0x0ff);
            Params[4] = (byte)(InactivityCloseTime & 0x0ff);

            // set it into request member variable
            request.HARTCommand = Params;
            // set the request's byte count
            request.m_HartCmdSize = 5;

            return(request);
        }
        /// <summary>
        /// Create a HART Close Session Request, send it to the Gateway,
        /// and close the socket.
        /// </summary>
        /// <param name="Result"><see cref="HARTMsgResult">HARTMsgResult</see></param>
        public void CloseSession(HARTMsgResult Result)
        {
            HartIPRequest Req = null;

            do
            {
                Req = HartIPRequest.CloseSession(TransactionId);
                MsgResponse MsgRsp = new MsgResponse(HARTIPConnect.USE_SOCKET_TIMEOUT_DEFAULT);
                if (SendHartRequest(Req, MsgRsp))
                {
                    try
                    {   // wait for the object return or timeout
                        if (MsgRsp.GetResponse() == false)
                        {
                            Result.AddMessage("Close Session failed: No response was received.", false, true);
                            break;
                        }
                        if (!MsgRsp.ResponseMsg.IsValidResponse)
                        {
                            Result.AddMessage("Close Session failed: Received an invalid response msg type.", false, true);
                            break;
                        }
                        LogMsg.Instance.Log(MsgRsp.ResponseMsg.ToString());
                    }
                    catch
                    {
                        Result.AddMessage("Close Session failed.", false, true);
                        break;
                    }
                }
                else
                {
                    Result.AddMessage("Close Session failed.", false, true);
                    break;
                }
            } while (false); /* ONCE */

            Close();
            Result.AddMessage("Closed the HPort socket.", false, true);
        }
        /// <summary>
        /// Create a HART Command Request
        /// </summary>
        /// <param name="usTranId">ushort Transaction Id</param>
        /// <param name="Command">byte[] Command byte array
        /// <para>Array should have frame, device address, command, byte count,
        /// data, and checksum bytes.</para>
        /// <para>device address is the device type and device id 5 bytes with expanded type mask</para>
        /// <remarks>See HART specification 081r8.2.pdf section 5.1, 5.2, and 5.3 for frame,
        /// address, expansion, data, and checksum bytes information.
        /// </remarks>
        /// </param>
        /// <param name="usByteCount">ushort the specified Command array byte count</param>
        /// <returns><see cref="HartIPRequest"> request</see></returns>
        public static HartIPRequest HartCommandRequest(ushort usTranId, byte[] Command,
                                                       ushort usByteCount, int Timeout = HARTIPConnect.USE_SOCKET_TIMEOUT_DEFAULT)
        {
            if (usByteCount > HARTIPMessage.MAX_REQUEST_MSG_LEN)
            {
                throw new ArgumentException(String.Format("HartCommandRequest Error: Invalid cmd length: {0}.",
                                                          usByteCount));
            }

            HartIPRequest Request = new HartIPRequest(MessageId.HART_WIRED_PDU, usTranId);

            Request.m_Timeout = Timeout;

            // if command has data
            if (usByteCount > 0)
            {
                Request.m_HartCmdSize = usByteCount;
                Request.HARTCommand   = Command;
            }

            return(Request);
        }
 /// <summary>
 /// Build a Hart-IP request
 /// </summary>
 /// <param name="Command">byte[] request Command byte array
 /// <para>Array should include the frame, device address, command, byte count,
 /// data, and checksum bytes.</para>para</param>
 /// <param name="usByteCount">ushort the specified Command array byte count</param>
 /// <returns><see cref="HartIPRequest"/></returns>
 public HartIPRequest BuildHartRequest(byte[] Command, ushort usByteCount)
 {
     return(HartIPRequest.HartCommandRequest(TransactionId, Command, usByteCount));
 }
        /// <summary>
        /// Initiate session with the network HART-IP device.
        /// </summary>
        /// <returns>bool</returns>
        private bool InitSession()
        {
            bool           bSuccess = false;
            HartIPResponse Response = null;

            do
            {
                // create init session request
                HartIPRequest Request = HartIPRequest.InitiateSession(TransactionId, m_InactivityCloseTime);
                if (!SendHartIPRequest(Request))
                {
                    LogMsg.Instance.Log("Error, sending InitSession request failure.", true);
                    Close();
                    break;
                }

                try
                {
                    Response = GetResponse();
                }
                catch (SocketException se)
                {
                    m_Error = String.Format("InitSession SocketException: ErrorCode:{0}. {1}.",
                                            se.ErrorCode, se.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                    Close();
                    break;
                }
                catch (Exception ex)
                {
                    m_Error = String.Format("InitSession Exception: {0}", ex.Message);
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                    Close();
                    break;
                }

                if (Response == null)
                {
                    m_Error = "Initiate Session failed getting response.";
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                    Close();
                    break;
                }
                else if (0 != Response.ResponseCode)
                {
                    m_Error = "InitSession received an error response code: " +
                              Response.ResponseCode.ToString() + "  " +
                              GetSessionInitStatusCode(Response.ResponseCode);

                    LogMsg.Instance.Log("Error, " + m_Error, true);
                    LogMsg.Instance.Log(Response.ToString());
                    Close();
                    break;
                }
                else if (!Response.IsValidResponse)
                {
                    m_Error = "InitSession received an invalid response Msg Type.";
                    LogMsg.Instance.Log("Error, " + m_Error, true);
                    LogMsg.Instance.Log(Response.ToString());
                    Close();
                    break;
                }

                LogMsg.Instance.Log(Response.ToString());
                bSuccess = (Response.Status == HARTIPMessage.RSP_SUCCESS ||
                            Response.Status == HARTIPMessage.RSP_SET_TO_NEAREST_POSSIBLE_VALUE) ? true : false;
            } while (false); /* ONCE */

            return(bSuccess);
        }
 /// <summary>
 /// Send a Hart-IP request to network HART-IP device.
 /// </summary>
 /// <param name="Request">HartIPRequest</param>
 /// <returns>bool True if it is success to send the HART-IP request.</returns>
 protected abstract bool SendRequest(HartIPRequest Request);
        /// <summary>
        /// Keep Alive Request
        /// </summary>
        /// <param name="usTranId">ushort Transaction Id</param>
        /// <returns><see cref="HartIPRequest"> keep alive request</see></returns>
        public static HartIPRequest KeepAlive(ushort usTranId)
        {
            HartIPRequest Request = new HartIPRequest(MessageId.KEEP_ALIVE, usTranId);

            return(Request);
        }
        /// <summary>
        /// Close Session Request
        /// </summary>
        /// <param name="usTranId">ushort Transaction Id</param>
        /// <returns><see cref="HartIPRequest">HartIPRequest close session request</see></returns>
        public static HartIPRequest CloseSession(ushort usTranId)
        {
            HartIPRequest HRequest = new HartIPRequest(MessageId.SESSION_CLOSE, usTranId);

            return(HRequest);
        }
        /// <summary>
        /// Send a Hart Request and it handles Delay Retry.
        /// If no more dr retry, it sends nerwork HART device a flush Dr cmd.
        /// </summary>
        /// <param name="Request"><see cref="HartIPRequest"/></param>
        /// <returns><see cref="HartIPResponse"/></returns>
        public HartIPResponse SendHartRequest(HartIPRequest Request)
        {
            HartIPResponse Rsp = null;

            if (Request == null)
            {
                throw new ArgumentException("HartIPRequest is null");
            }

            if ((m_HartIPConn == null) || !m_HartIPConn.IsConnected)
            {
                throw new Exception("Call Connect to initialize the network connection first.");
            }

            lock (SyncRoot)
            {
                m_Error = String.Empty;

                // send the first request
                Rsp = SendRequest(Request);

                if (Rsp != null)
                {
                    Logger.Log(Rsp.ToString());
                }

                if ((Rsp != null) &&
                    ((Rsp.ResponseCode == HARTIPMessage.RSP_DR_INITIATE) ||
                     (Rsp.ResponseCode == HARTIPMessage.RSP_DR_CONFLICT) ||
                     (Rsp.ResponseCode == HARTIPMessage.RSP_DR_RUNNING) ||
                     (Rsp.ResponseCode == HARTIPMessage.RSP_DEVICE_BUSY)))
                {
                    if (m_nDrRetries > 0)
                    {
                        Rsp = null;
                        // Create a request task to store the request and retries values.
                        RequestTask ReqTask = new RequestTask(Request, m_nDrRetries,
                                                              m_nDrRetryDelay);

                        // Create a request worker to do the task with the callback to handles the retries.
                        RequestWorker ReqWorker = new RequestWorker(ReqTask,
                                                                    new RequestWorker.HandleSendRequestDelegate(HandleSendHARTIPRequest));
                        try
                        {
                            // start the task
                            ReqWorker.Perform();
                            Rsp = ReqTask.HartIPRsp;
                        }
                        catch (UserCancelException)
                        {
                            // user canceled action
                            m_Error = "User canceled sending retries command.";
                        }
                    }
                    else
                    {
                        SendFlushDrCmd();
                    }
                }
            }

//           if (Rsp.Command == 77)
//           { // unwrap the HART PDU in the response data
//                 Rsp
//           }

            return(Rsp);
        }
        /// <summary>
        /// Send a Hart Request and handle Delay Retry.
        /// If no more dr retry, it sends nerwork HART device a flush Dr cmd.
        /// </summary>
        /// <param name="Request">byte[] a request byte array</param>
        /// <param name="ByteCount">ushort request array byte count</param>
        /// <param name="Timeout">int request timeout in ms</param>        /// <returns><see cref="HartIPResponse"/></returns>
        public HartIPResponse SendHartRequest(byte[] Request, ushort ByteCount, int Timeout = HARTIPConnect.USE_SOCKET_TIMEOUT_DEFAULT)
        {
            HartIPRequest Req = HartIPRequest.HartCommandRequest(m_HartIPConn.TransactionId, Request, ByteCount, Timeout);

            return(SendHartRequest(Req));
        }
        /// <summary>
        /// Build a HartIP Request using the specified request command, request message,
        /// device type, and device id. It will have frame, device address, command, byte count,
        /// data, and checksum bytes in the returned HartIPRequest.Command.
        /// </summary>
        /// <param name="usReqCmd">ushort Request command</param>
        /// <param name="ReqMsg">String Request message in Hex string</param>
        /// <param name="usDeviceType">ushort Device Type</param>
        /// <param name="nDeviceId">uint Device ID</param>
        /// <returns><see cref="HartIPRequest"/></returns>
        public HartIPRequest BuildHartIPRequest(ushort usReqCmd, String ReqMsg, ushort usDeviceType,
                                                uint nDeviceId)
        {
            String        Msg;
            HartIPRequest HRequest = null;
            int           nDataLen = 0;

            if (m_HartIPConn == null)
            {
                throw new Exception("Call Connect to initialize the network connection first.");
            }

            if ((uint)(usReqCmd) > 65536)
            {
                Msg = "Invalid HARTIP Request Command.";
                throw new ArgumentException(Msg);
            }

            if (!String.IsNullOrEmpty(ReqMsg))
            {
                ReqMsg   = ReqMsg.Replace(" ", "");
                nDataLen = ReqMsg.Length;
                if ((nDataLen > 0) && (nDataLen % 2) != 0)
                {
                    Msg = "Multiple contiguous bytes must define an even number of hex digits.";
                    throw new ArgumentException(Msg);
                }
            }

            // Check if it is extended command
            bool bExtendedCmd   = (usReqCmd > HARTIPMessage.MAX_SINGLE_BYTE_CMD) ? true : false;
            byte cCmd           = (byte)(bExtendedCmd ? HARTIPMessage.CMD_EXTENDED_CMD : usReqCmd);
            byte cbyteCount     = 9;
            byte cDataByteCount = 0;
            int  nIndex         = 0;
            int  nSubCmdByteCountIndex;

            // Request bytes
            byte[] Data = new byte[HARTIPMessage.MAX_REQUEST_MSG_LEN];
            Array.Clear(Data, 0, Data.Length);

            if (nDataLen > 0)
            {
                cbyteCount    += (byte)(nDataLen / 2);
                cDataByteCount = (byte)(nDataLen / 2);
            }

            // form a request
            Data[nIndex++]        = 0x82; // long frame
            Data[nIndex++]        = (byte)(((usDeviceType >> 8) & 0x3F) | 0x80);
            Data[nIndex++]        = (byte)(usDeviceType & 0x0ff);
            Data[nIndex++]        = (byte)((nDeviceId >> 16) & 0x0ff);
            Data[nIndex++]        = (byte)((nDeviceId >> 8) & 0x0ff);
            Data[nIndex++]        = (byte)(nDeviceId & 0x0ff);
            Data[nIndex++]        = cCmd; // cmd
            nSubCmdByteCountIndex = nIndex;
            // skip the byte count
            nIndex += 1;

            // if it is extended cmd, put the cmd two bytes in data field first
            if (bExtendedCmd)
            {
                Data[nIndex++]  = (byte)((usReqCmd >> 8) & 0x0ff);
                Data[nIndex++]  = (byte)(usReqCmd & 0x0ff);
                cbyteCount     += 2;
                cDataByteCount += 2;
            }

            // set the data byte count value
            Data[nSubCmdByteCountIndex] = cDataByteCount;

            int  n = 0;
            byte cY, cW;

            while (n < nDataLen)
            {
                if (HartUtil.HexCharToNibble(ReqMsg[n], out cY))
                {
                    if (HartUtil.HexCharToNibble(ReqMsg[n + 1], out cW))
                    {
                        Data[nIndex++] = (byte)(cY * 16 + cW);
                        n += 2;
                    }
                    else
                    {
                        Msg = String.Format("The character: '{0}' is not allowed in the request data byte value.",
                                            ReqMsg[n + 1]);
                        throw new ArgumentException(Msg);
                    }
                }
                else
                {
                    Msg = String.Format("The character: '{0}' is not allowed in the request data byte value.",
                                        ReqMsg[n]);
                    throw new ArgumentException(Msg);
                }
            }

            // tunnel messages to attached devices
            if (RootDevice.DeviceType != usDeviceType && RootDevice.DeviceId != nDeviceId && RootDevice.IsBridgeDevice)
            { // message is routed into an attached device
                // wrap the message with command 77 to tunnel to attached device
                byte[] Data77 = new byte[HARTIPMessage.MAX_REQUEST_MSG_LEN];
                Array.Clear(Data77, 0, Data.Length);

                // command 77 request data
                HartDevice dev = FindDevice(nDeviceId);
                Data77[0] = dev.IOCard;
                Data77[1] = dev.Channel;
                Data77[2] = 5;  // transmit preamble count
                Array.Copy(Data, 0, Data77, 3, nIndex);
                nIndex += 3;

                // send the command 77 to the root device (a gateway or IO)
                string hex = BitConverter.ToString(Data77).Replace("-", string.Empty);
                hex = hex.Substring(0, nIndex * 2);
                return(BuildHartIPRequest(77, hex, RootDevice.DeviceType, RootDevice.DeviceId));
            }

            // Get check sum byte and send the request
            Data[nIndex] = HartUtil.GetCheckSum(Data, (byte)(cbyteCount - 1));
            HRequest     = HartIPRequest.HartCommandRequest(m_HartIPConn.TransactionId,
                                                            Data, cbyteCount);

            return(HRequest);
        }