OPC UA中的幾個超時(Timeout)

OPC UA交互模型

在OPC UA中有個"服務"的概念,注意這個"服務"和操作系統中的"服務"不是一個概念。在OPC UA中"服務"表示的是實現一個功能的一組方法,如:查找服務器服務集、獲得終端服務集、客戶端與服務器連接管理服務集、讀寫數據及元數據服務集等。而服務又以請求的方式去實現。只不過這部分OPC 棧都給我們封裝好了,我們在使用時不需要關心,但是會涉及到標題提到的超時時間。有精力的話可以看看源碼,比如TCP發送請求:

   public IAsyncResult BeginSendRequest(IServiceRequest request, int timeout, AsyncCallback callback, object state)
        {
            if (request == null) throw new ArgumentNullException("request");
            if (timeout <= 0) throw new ArgumentException("Timeout must be greater than zero.", "timeout");

            lock (DataLock)
            {
                bool firstCall = false;
                WriteOperation operation = null;
                
                // check if this is the first call.
                if (State == TcpChannelState.Closed)
                {
                    if (m_queuedOperations == null)
                    {
                        firstCall = true;
                        m_queuedOperations = new List<QueuedOperation>();
                    }
                }

                // queue operations until connect completes.
                if (m_queuedOperations != null)
                {
                    operation = BeginOperation(timeout, callback, state);
                    m_queuedOperations.Add(new QueuedOperation(operation, timeout, request));

                    if (firstCall)
                    {
                        BeginConnect(this.m_url, timeout, OnConnectOnDemandComplete, null);
                    }

                    return operation;
                }

                if (State != TcpChannelState.Open)
                {
                    throw new ServiceResultException(StatusCodes.BadConnectionClosed);
                }

                // Utils.Trace("Channel {0}: BeginSendRequest()", ChannelId);

                if (m_reconnecting)
                {
                    throw ServiceResultException.Create(StatusCodes.BadRequestInterrupted, "Attempting to reconnect to the server.");
                }

                // send request.
                operation = BeginOperation(timeout, callback, state);
                SendRequest(operation, timeout, request);
                return operation;
            }
        }

廢話不多說了,來說正題-超時。

1.請求超時(OperationTimeout)

    請求超時是在應用配置(ApplicationConfiguration)-傳輸配額配置(TransportQuotas)-操作超時(OperationTimeout)屬性中指定的。

     它表示的是請求超時,由客戶端配置。如果超時時間到期,客戶端通訊協議棧返回API調用,或者發送超時狀態回調。但需要注意的是,客戶端UA棧設置的超時時間也會發送給服務器,用於探測不在需要返回給客戶端的調用。

2.會話默認超時時間(DefaultSessionTimeout)

     會話默認超時可以在應用配置(ApplicationConfiguration)-客戶端配置(ClientConfiguration)-默認會話超時(DefaultSessionTimeout)屬性中指定。

     會話默認超時只的是在當前客戶端中創建的會話的默認超時。

3.會話超時(sessionTimeout)

    會話超時是在創建會話時指定。

 public static Session Create(
            ApplicationConfiguration configuration,
            ConfiguredEndpoint endpoint,
            bool updateBeforeConnect,
            string sessionName,
            uint sessionTimeout,   //會話超時
            IUserIdentity identity,
            IList<string> preferredLocales)
        {
            return Create(configuration, endpoint, updateBeforeConnect, false, sessionName, sessionTimeout, identity, preferredLocales);
        }

    會話超時和默認會話超時時間其實指的是一個東西,在創建會話時指定時如果指定了會話超時時間就是用指定的會話超時時間,如果在創建會話時沒有指定會話超時時間(爲0),默認會話超時時間就會起作用。源代碼中是這樣寫的

            if (sessionTimeout == 0)
            {
                sessionTimeout = (uint)m_configuration.ClientConfiguration.DefaultSessionTimeout;
            }

    講了這麼多,還沒說這個會話超時具體是什麼意義。對於這個會話超時,官方是這麼解釋的:如果一個客戶端在約定的時間內沒有發出一個請求,會話將會被服務器自動終止。如果想了解具體是怎麼幹的,還是需要去看看元代碼,如果僅僅是開發使用瞭解這麼多久足夠了。

4.修訂會話超時(revisedSessionTimeout )

    這個是服務端的響應返回,我們做客戶端開發時不需要太關心,但是最好還是需要了解下。官方的解釋爲:服務器分配的會話超時,如果客戶端請求的超時在服務器定義的有效範圍內,就是用該超時。 這理解起來可能有些晦澀,其實是這樣的:在創建會話時客戶端可以指定一個會話超時時間,服務器也有個會話超時時間,這就有了個約定客戶端指定的會話超時時間必須在服務器規定的會話超時時間範圍內,否則就會取服務端會話超時上下限。經測試,Kepserver6.4集成的UA 服務端會話超時範圍爲15000-60000.也就是說如果你在創建會話時超時時間指定爲的值爲15000-60000時那麼會話超時時間爲客戶端設定值,如果創建會話時超時時間設定爲<15000的值那麼實際超時時間爲15000,如果創建會話時超時時間設定爲>60000,那麼實際會話超時時間爲60000.實際超時時間會由 修訂會話超時(revisedSessionTimeout )這個參數返回。它的實現爲:

        public virtual ResponseHeader CreateSession(
            RequestHeader                           requestHeader,
            ApplicationDescription                  clientDescription,
            string                                  serverUri,
            string                                  endpointUrl,
            string                                  sessionName,
            byte[]                                  clientNonce,
            byte[]                                  clientCertificate,
            double                                  requestedSessionTimeout,
            uint                                    maxResponseMessageSize,
            out NodeId                              sessionId,
            out NodeId                              authenticationToken,
            out double                              revisedSessionTimeout,
            out byte[]                              serverNonce,
            out byte[]                              serverCertificate,
            out EndpointDescriptionCollection       serverEndpoints,
            out SignedSoftwareCertificateCollection serverSoftwareCertificates,
            out SignatureData                       serverSignature,
            out uint                                maxRequestMessageSize)
        {
            CreateSessionRequest request = new CreateSessionRequest();
            CreateSessionResponse response = null;

            request.RequestHeader           = requestHeader;
            request.ClientDescription       = clientDescription;
            request.ServerUri               = serverUri;
            request.EndpointUrl             = endpointUrl;
            request.SessionName             = sessionName;
            request.ClientNonce             = clientNonce;
            request.ClientCertificate       = clientCertificate;
            request.RequestedSessionTimeout = requestedSessionTimeout;
            request.MaxResponseMessageSize  = maxResponseMessageSize;

            UpdateRequestHeader(request, requestHeader == null, "CreateSession");

            try
            {
                if (UseTransportChannel)
                {
                    IServiceResponse genericResponse = TransportChannel.SendRequest(request);

                    if (genericResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }

                    ValidateResponse(genericResponse.ResponseHeader);
                    response = (CreateSessionResponse)genericResponse;
                }
                else
                {
                    CreateSessionResponseMessage responseMessage = InnerChannel.CreateSession(new CreateSessionMessage(request));

                    if (responseMessage == null || responseMessage.CreateSessionResponse == null)
                    {
                        throw new ServiceResultException(StatusCodes.BadUnknownResponse);
                    }

                    response = responseMessage.CreateSessionResponse;
                    ValidateResponse(response.ResponseHeader);
                }

                sessionId                  = response.SessionId;
                authenticationToken        = response.AuthenticationToken;
                revisedSessionTimeout      = response.RevisedSessionTimeout;
                serverNonce                = response.ServerNonce;
                serverCertificate          = response.ServerCertificate;
                serverEndpoints            = response.ServerEndpoints;
                serverSoftwareCertificates = response.ServerSoftwareCertificates;
                serverSignature            = response.ServerSignature;
                maxRequestMessageSize      = response.MaxRequestMessageSize;
            }
            finally
            {
                RequestCompleted(request, response, "CreateSession");
            }

            return response.ResponseHeader;
        }

 5.訂閱發佈時間間隔(PublishingInterval)

    訂閱發佈時間間隔在訂閱對象屬性中指定:

       /// <summary>
        /// The publishing interval.
        /// </summary>
        [DataMember(Order = 2)]
        public int PublishingInterval
        {
            get { return m_publishingInterval;  }
            set { m_publishingInterval = value; }
        }

    訂閱發佈時間間隔控制訂閱最少多少時間通知我們一次。

6.訂閱採樣時間(SamplingInterval)

    訂閱採樣時間在訂閱條目對象中指定:

 

       /// <summary>
        /// The sampling interval.
        /// </summary>
        [DataMember(Order = 9)]
        public int SamplingInterval
        {
            get { return m_samplingInterval; }
            
            set 
            {
                if (m_samplingInterval != value)
                {
                    m_attributesModified = true;
                }

                m_samplingInterval = value; 
            }
        }

    訂閱採樣時間指的是訂閱的條目的採樣時間。

訂閱發佈時間和訂閱採樣時間這麼講理解起來可能有點兒模糊。所以我去找了張圖一起來學習一下吧。

看了這張圖是不是就很清楚了。原來訂閱條目採樣後並不是直接發佈出來,而是要進自己的隊列。這個隊列大小在我們初始化訂閱條目時也是可以指定的。

        /// <summary>
        /// The length of the queue used to buffer values.
        /// </summary>
        [DataMember(Order = 11)]
        public uint QueueSize
        {
            get { return m_queueSize; }
            
            set 
            {
                if (m_queueSize != value)
                {
                    m_attributesModified = true;
                }

                m_queueSize = value; 
            }
        }

 

說明:以上所有代碼來自OPC 基金會官方 OPC UA開源庫。歡迎大家一起學習、討論:QQ羣-633204942

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章