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