Errata exists for this version of the document.
Subscriptions are used to report Notifications to the Client. Their general behaviour is summarized below. Their precise behaviour is described in 5.13.1.2.
- Subscriptions have a set of MonitoredItems assigned to them by the Client. MonitoredItems generate Notifications that are to be reported to the Client by the Subscription (see 5.12.1 for a description of MonitoredItems).
- Subscriptions have a publishing interval. The publishing interval of a Subscription defines the cyclic rate at which the Subscription executes. Each time it executes, it attempts to send a NotificationMessage to the Client. NotificationMessages contain Notifications that have not yet been reported to Client.
- NotificationMessages are sent to the Client in response to Publish requests. Publish requests are normally queued to the Session as they are received, and one is de-queued and processed by a subscription related to this Session for each publishing cycle, if there are Notifications to report. When there are not, the Publish request is not de-queued from the Session, and the Server waits until the next cycle and checks again for Notifications.
- At the beginning of a cycle, if there are Notifications to send but there are no Publish requests queued, the Server enters a wait state for a Publish request to be received. When one is received, it is processed immediately without waiting for the next publishing cycle.
- NotificationMessages are uniquely identified by sequence numbers that enable Clients to detect missed Messages. The publishing interval also defines the default sampling interval for its MonitoredItems.
- Subscriptions have a keep-alive counter that counts the number of consecutive publishing cycles in which there have been no Notifications to report to the Client. When the maximum keep-alive count is reached, a Publish request is de-queued and used to return a keep-alive Message. This keep-alive Message informs the Client that the Subscription is still active. Each keep-alive Message is a response to a Publish request in which the notificationMessage parameter does not contain any Notifications and that contains the sequence number of the next NotificationMessage that is to be sent. In the clauses that follow, the term NotificationMessage refers to a response to a Publish request in which the notificationMessage parameter actually contains one or more Notifications, as opposed to a keep-alive Message in which this parameter contains no Notifications. The maximum keep-alive count is set by the Client during Subscription creation and may be subsequently modified using the ModifySubscription Service. Similar to Notification processing described in (c) above, if there are no Publish requests queued, the Server waits for the next one to be received and sends the keep-alive immediately without waiting for the next publishing cycle.
- Publishing by a Subscription may be enabled or disabled by the Client when created, or subsequently using the SetPublishingMode Service. Disabling causes the Subscription to cease sending NotificationMessages to the Client. However, the Subscription continues to execute cyclically and continues to send keep-alive Messages to the Client.
- Subscriptions have a lifetime counter that counts the number of consecutive publishing cycles in which there have been no Publish requests available to send a Publish response for the Subscription. Any Service call that uses the SubscriptionId or the processing of a Publish response resets the lifetime counter of this Subscription. When this counter reaches the value calculated for the lifetime of a Subscription based on the MaxKeepAliveCount parameter in the CreateSubscription Service (5.13.2), the Subscription is closed. Closing the Subscription causes its MonitoredItems to be deleted. In addition the Server shall issue a StatusChangeNotification notificationMessage with the status code Bad_Timeout. The StatusChangeNotification notificationMessage type is defined in 7.20.4.
- Sessions maintain a retransmission queue of sent NotificationMessages. NotificationMessages are retained in this queue until they are acknowledged. The Session shall maintain a retransmission queue size of at least two times the number of Publish requests per Session the Server supports. The minimum size of the retransmission queue may be changed by a Profile in OPC 10000-7. The minimum number of Publish requests per Session the Server shall support is defined in OPC 10000-7. Clients are required to acknowledge NotificationMessages as they are received. In the case of a retransmission queue overflow, the oldest sent NotificationMessage gets deleted. If a Subscription is transferred to another Session, the queued NotificationMessages for this Subscription are moved from the old to the new Session.
The sequence number is an unsigned 32-bit integer that is incremented by one for each NotificationMessage sent. The value 0 is never used for the sequence number. The first NotificationMessage sent on a Subscription has a sequence number of 1. If the sequence number rolls over, it rolls over to 1.
When a Subscription is created, the first Message is sent at the end of the first publishing cycle to inform the Client that the Subscription is operational. A NotificationMessage is sent if there are Notifications ready to be reported. If there are none, a keep-alive Message is sent instead that contains a sequence number of 1, indicating that the first NotificationMessage has not yet been sent. This is the only time a keep-alive Message is sent without waiting for the maximum keep-alive count to be reached, as specified in (f) above.
A Client shall be prepared for receiving Publish responses for a Subscription more frequently than the corresponding publishing interval. One example is the situation where the number of available notifications exceeds the Subscription setting maxNotificationsPerPublish. A Client is always able to control the timing of the Publish responses by not queueing Publish requests. If a Client does not queue Publish requests in the Server, the Server can only send a Publish response if it receives a new Publish request. This would increase latency for delivery of notifications but allows a Client to throttle the number of received Publish responses in high load situations.
The value of the sequence number is never reset during the lifetime of a Subscription. Therefore, the same sequence number shall not be reused on a Subscription until over four billion NotificationMessages have been sent. At a continuous rate of one thousand NotificationMessages per second on a given Subscription, it would take roughly fifty days for the same sequence number to be reused. This allows Clients to safely treat sequence numbers as unique.
Sequence numbers are also used by Clients to acknowledge the receipt of NotificationMessages. Publish requests allow the Client to acknowledge all Notifications up to a specific sequence number and to acknowledge the sequence number of the last NotificationMessage received. One or more gaps may exist in between. Acknowledgements allow the Server to delete NotificationMessages from its retransmission queue.
Clients may ask for retransmission of selected NotificationMessages using the Republish Service. This Service returns the requested Message.
Subscriptions are designed to work independent of the actual communication connection between OPC UA Client and Server and independent of a Session. Short communication interruptions can be handled without losing data or events. To make sure that longer communication interruptions or planned disconnects can be handled without losing data or events, an OPC UA Server may support durable Subscriptions. If this feature is supported, the Server accepts a high Subscription RequestedLifetimeCount and large MonitoredItem QueueSize parameter settings. Clause 6.8 describes how durable Subscriptions can be created and used.
The state table formally describes the operation of the Subscription. The following model of operations is described by this state table. This description applies when publishing is enabled or disabled for the Subscription.
After creation of the Subscription, the Server starts the publishing timer and restarts it whenever it expires. If the timer expires the number of times defined for the Subscription lifetime without having received a Subscription Service request from the Client, the Subscription assumes that the Client is no longer present, and terminates.
Clients send Publish requests to Servers to receive Notifications. Publish requests are not directed to any one Subscription and, therefore, may be used by any Subscription. Each contains acknowledgements for one or more Subscriptions. These acknowledgements are processed when the Publish request is received. The Server then queues the request in a queue shared by all Subscriptions, except in the following cases.
- The previous Publish response indicated that there were still more Notifications ready to be transferred and there were no more Publish requests queued to transfer them.
- The publishing timer of a Subscription expired and there were either Notifications to be sent or a keep-alive Message to be sent.
In these cases, the newly received Publish request is processed immediately by the first Subscription to encounter either case (a) or case (b).
Each time the publishing timer expires, it is immediately reset. If there are Notifications or a keep-alive Message to be sent, it de-queues and processes a Publish request. When a Subscription processes a Publish request, it accesses the queues of its MonitoredItems and de-queues its Notifications, if any. It returns these Notifications in the response, setting the moreNotifications flag if it was not able to return all available Notifications in the response.
If there were Notifications or a keep-alive Message to be sent but there were no Publish requests queued, the Subscription assumes that the Publish request is late and waits for the next Publish request to be received, as described in case (b).
If the Subscription is disabled when the publishing timer expires or if there are no Notifications available, it enters the keep-alive state and sets the keep-alive counter to its maximum value as defined for the Subscription.
While in the keep-alive state, it checks for Notifications each time the publishing timer expires. If one or more Notifications have been generated, a Publish request is de-queued and a NotificationMessage is returned in the response. However, if the publishing timer expires without a Notification becoming available, a Publish request is de-queued and a keep-alive Message is returned in the response. The Subscription then returns to the normal state of waiting for the publishing timer to expire again. If, in either of these cases, there are no Publish requests queued, the Subscription waits for the next Publish request to be received, as described in case (b).
The Subscription states are defined in Table 84.
Table 84 – Subscription States
State |
Description |
CLOSED |
The Subscription has not yet been created or has terminated. |
CREATING |
The Subscription is being created. |
NORMAL |
The Subscription is cyclically checking for Notifications from its MonitoredItems. The keep-alive counter is not used in this state. |
LATE |
The publishing timer has expired and there are Notifications available or a keep-alive Message is ready to be sent, but there are no Publish requests queued. When in this state, the next Publish request is processed when it is received. The keep-alive counter is not used in this state. |
KEEPALIVE |
The Subscription is cyclically checking for Notifications from its MonitoredItems or for the keep-alive counter to count down to 0 from its maximum. |
The state table is described in Table 85. The following rules and conventions apply.
- Events represent the receipt of Service requests and the occurrence internal Events, such as timer expirations.
- Service requests Events may be accompanied by conditions that test Service parameter values. Parameter names begin with a lower case letter.
- Internal Events may be accompanied by conditions that test state Variable values. State Variables are defined in 5.13.1.3. They begin with an upper case letter.
- Service request and internal Events may be accompanied by conditions represented by functions whose return value is tested. Functions are identified by “()” after their name. They are described in 5.13.1.4.
- When an Event is received, the first transition for the current state is located and the transitions are searched sequentially for the first transition that meets the Event or conditions criteria. If none are found, the Event is ignored.
- Actions are described by functions and state Variable manipulations.
- The LifetimeTimerExpires Event is triggered when its corresponding counter reaches zero.
Table 85 – Subscription State Table
# |
Current State |
Event/Conditions |
Action |
Next State |
1 |
CLOSED |
Receive CreateSubscription Request |
CreateSubscription() |
CREATING |
2 |
CREATING |
CreateSubscription fails |
ReturnNegativeResponse() |
CLOSED |
3 |
CREATING |
CreateSubscription succeeds |
InitializeSubscription() MessageSent = FALSE ReturnResponse() |
NORMAL |
4 |
NORMAL |
Receive Publish Request && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && MoreNotifications == FALSE) ) |
DeleteAckedNotificationMsgs() EnqueuePublishingReq() |
NORMAL |
5 |
NORMAL |
Receive Publish Request && PublishingEnabled == TRUE && MoreNotifications == TRUE |
ResetLifetimeCounter() DeleteAckedNotificationMsgs() ReturnNotifications() MessageSent = TRUE |
NORMAL |
6 |
NORMAL |
PublishingTimer Expires && PublishingReqQueued == TRUE && PublishingEnabled == TRUE && NotificationsAvailable == TRUE |
ResetLifetimeCounter() StartPublishingTimer() DequeuePublishReq() ReturnNotifications() MessageSent == TRUE |
NORMAL |
7 |
NORMAL |
PublishingTimer Expires && PublishingReqQueued == TRUE && MessageSent == FALSE && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == FALSE) ) |
ResetLifetimeCounter() StartPublishingTimer() DequeuePublishReq() ReturnKeepAlive() MessageSent == TRUE |
NORMAL |
8 |
NORMAL |
PublishingTimer Expires && PublishingReqQueued == FALSE && ( MessageSent == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == TRUE) ) |
StartPublishingTimer() |
LATE |
9 |
NORMAL |
PublishingTimer Expires && MessageSent == TRUE && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == FALSE) ) |
StartPublishingTimer() ResetKeepAliveCounter() |
KEEPALIVE |
10 |
LATE |
Receive Publish Request && PublishingEnabled == TRUE && (NotificationsAvailable == TRUE || MoreNotifications == TRUE) |
ResetLifetimeCounter() DeleteAckedNotificationMsgs() ReturnNotifications() MessageSent = TRUE |
NORMAL |
11 |
LATE |
Receive Publish Request && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == FALSE && MoreNotifications == FALSE) ) |
ResetLifetimeCounter() DeleteAckedNotificationMsgs() ReturnKeepAlive() MessageSent = TRUE |
KEEPALIVE |
12 |
LATE |
PublishingTimer Expires |
StartPublishingTimer() |
LATE |
13 |
KEEPALIVE |
Receive Publish Request |
DeleteAckedNotificationMsgs() EnqueuePublishingReq() |
KEEPALIVE |
14 |
KEEPALIVE |
PublishingTimer Expires && PublishingEnabled == TRUE && NotificationsAvailable == TRUE && PublishingReqQueued == TRUE |
ResetLifetimeCounter() StartPublishingTimer() DequeuePublishReq() ReturnNotifications() MessageSent == TRUE |
NORMAL |
15 |
KEEPALIVE |
PublishingTimer Expires && PublishingReqQueued == TRUE && KeepAliveCounter == 1 && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == FALSE ) |
StartPublishingTimer() DequeuePublishReq() ReturnKeepAlive() ResetKeepAliveCounter() |
KEEPALIVE |
16 |
KEEPALIVE |
PublishingTimer Expires && KeepAliveCounter > 1 && ( PublishingEnabled == FALSE || (PublishingEnabled == TRUE && NotificationsAvailable == FALSE) ) |
StartPublishingTimer() KeepAliveCounter-- |
KEEPALIVE |
17 |
KEEPALIVE |
PublishingTimer Expires && PublishingReqQueued == FALSE && ( KeepAliveCounter == 1 || (KeepAliveCounter > 1 && PublishingEnabled == TRUE && NotificationsAvailable == TRUE) ) |
StartPublishingTimer() |
LATE |
18 |
NORMAL || LATE || KEEPALIVE |
Receive ModifySubscription Request |
ResetLifetimeCounter() UpdateSubscriptionParams() ReturnResponse() |
SAME |
19 |
NORMAL || LATE || KEEPALIVE |
Receive SetPublishingMode Request |
ResetLifetimeCounter() SetPublishingEnabled() MoreNotifications = FALSE ReturnResponse() |
SAME |
20 |
NORMAL || LATE || KEEPALIVE |
Receive Republish Request && RequestedMessageFound == TRUE |
ResetLifetimeCounter() ReturnResponse() |
SAME |
21 |
NORMAL || LATE || KEEPALIVE |
Receive Republish Request && RequestedMessageFound == FALSE |
ResetLifetimeCounter() ReturnNegativeResponse() |
SAME |
22 |
NORMAL || LATE || KEEPALIVE |
Receive TransferSubscriptions Request && SessionChanged() == FALSE |
ResetLifetimeCounter() ReturnNegativeResponse () |
SAME |
23 |
NORMAL || LATE || KEEPALIVE |
Receive TransferSubscriptions Request && SessionChanged() == TRUE && ClientValidated() ==TRUE |
SetSession() ResetLifetimeCounter() ReturnResponse() IssueStatusChangeNotification() |
SAME |
24 |
NORMAL || LATE || KEEPALIVE |
Receive TransferSubscriptions Request && SessionChanged() == TRUE && ClientValidated() == FALSE |
ReturnNegativeResponse() |
SAME |
25 |
NORMAL || LATE || KEEPALIVE |
Receive DeleteSubscriptions Request && SubscriptionAssignedToClient ==TRUE |
DeleteMonitoredItems() DeleteClientPublReqQueue() |
CLOSED |
26 |
NORMAL || LATE || KEEPALIVE |
Receive DeleteSubscriptions Request && SubscriptionAssignedToClient ==FALSE |
ResetLifetimeCounter() ReturnNegativeResponse() |
SAME |
27 |
NORMAL || LATE || KEEPALIVE |
LifetimeCounter == 1 The LifetimeCounter is decremented if PublishingTimer expires and PublishingReqQueued == FALSE The LifetimeCounter is reset if PublishingReqQueued == TRUE. |
DeleteMonitoredItems() IssueStatusChangeNotification() |
CLOSED |
The state Variables are defined alphabetically in Table 86.
Table 86 – State variables and parameters
State Variable |
Description |
MoreNotifications |
A boolean value that is set to TRUE only by the CreateNotificationMsg() when there were too many Notifications for a single NotificationMessage. |
LatePublishRequest |
A boolean value that is set to TRUE to reflect that, the last time the publishing timer expired, there were no Publish requests queued. |
LifetimeCounter |
A value that contains the number of consecutive publishing timer expirations without Client activity before the Subscription is terminated. |
MessageSent |
A boolean value that is set to TRUE to mean that either a NotificationMessage or a keep-alive Message has been sent on the Subscription. It is a flag that is used to ensure that either a NotificationMessage or a keep-alive Message is sent out the first time the publishing timer expires. |
NotificationsAvailable |
A boolean value that is set to TRUE only when there is at least one MonitoredItem that is in the reporting mode and that has a Notification queued or there is at least one item to report whose triggering item has triggered and that has a Notification queued. The transition of this state Variable from FALSE to TRUE creates the “New Notification Queued” Event in the state table. |
PublishingEnabled |
The parameter that requests publishing to be enabled or disabled. |
PublishingReqQueued |
A boolean value that is set to TRUE only when there is a Publish request Message queued to the Subscription. |
RequestedMessageFound |
A boolean value that is set to TRUE only when the Message requested to be retransmitted was found in the retransmission queue. |
SeqNum |
The value that records the value of the sequence number used in NotificationMessages. |
SubscriptionAssignedToClient |
A boolean value that is set to TRUE only when the Subscription requested to be deleted is assigned to the Client that issued the request. A Subscription is assigned to the Client that created it. That assignment can only be changed through successful completion of the TransferSubscriptions Service. |
The action functions are defined alphabetically in Table 87.
Function |
Description |
ClientValidated() |
A boolean function that returns TRUE only when the Client that is submitting a TransferSubscriptions request is operating on behalf of the same user and supports the same Profiles as the Client of the previous Session. |
CreateNotificationMsg() |
Increment the SeqNum and create a NotificationMessage from the MonitoredItems assigned to the Subscription. Save the newly-created NotificationMessage in the retransmission queue. If all available Notifications can be sent in the Publish response, the MoreNotifications state Variable is set to FALSE. Otherwise, it is set to TRUE. |
CreateSubscription() |
Attempt to create the Subscription. |
DeleteAckedNotificationMsgs() |
Delete the NotificationMessages from the retransmission queue that were acknowledged by the request. |
DeleteClientPublReqQueue() |
Clear the Publish request queue for the Client that is sending the DeleteSubscriptions request, if there are no more Subscriptions assigned to that Client. |
DeleteMonitoredItems() |
Delete all MonitoredItems assigned to the Subscription. |
DequeuePublishReq() |
De-queue a publishing request in first-in first-out order. Validate if the publish request is still valid by checking the timeoutHint in the RequestHeader. If the request timed out, send a Bad_Timeout service result for the request and de-queue another publish request. ResetLifetimeCounter() |
EnqueuePublishingReq() |
Enqueue the publishing request. |
InitializeSubscription() |
ResetLifetimeCounter() MoreNotifications = FALSE PublishRateChange = FALSE PublishingEnabled = value of publishingEnabled parameter in the CreateSubscription request PublishingReqQueued = FALSE SeqNum = 0 SetSession() StartPublishingTimer() |
IssueStatusChangeNotification() |
Issue a StatusChangeNotification notificationMessage with a status code for the status change of the Subscription. The StatusChangeNotification notificationMessage type is defined in 7.20.4. Bad_Timeout status code is used if the lifetime expires and Good_SubscriptionTransferred is used if the Subscriptions was transferred to another Session. |
ResetKeepAliveCounter() |
Reset the keep-alive counter to the maximum keep-alive count of the Subscription. The maximum keep-alive count is set by the Client when the Subscription is created and may be modified using the ModifySubscription Service. |
ResetLifetimeCounter() |
Reset the LifetimeCounter Variable to the value specified for the lifetime of a Subscription in the CreateSubscription Service (5.13.2). |
ReturnKeepAlive() |
CreateKeepAliveMsg() ReturnResponse() |
ReturnNegativeResponse () |
Return a Service response indicating the appropriate Service level error. No parameters are returned other than the responseHeader that contains the Service level StatusCode. |
ReturnNotifications() |
CreateNotificationMsg() ReturnResponse() If (MoreNotifications == TRUE) && (PublishingReqQueued == TRUE) { DequeuePublishReq() Loop through this function again } |
ReturnResponse() |
Return the appropriate response, setting the appropriate parameter values and StatusCodes defined for the Service. |
SessionChanged() |
A boolean function that returns TRUE only when the Session used to send a TransferSubscriptions request is different from the Client Session currently associated with the Subscription. |
SetPublishingEnabled () |
Set the PublishingEnabled state Variable to the value of the publishingEnabled parameter received in the request. |
SetSession |
Set the Session information for the Subscription to match the Session on which the TransferSubscriptions request was issued. |
StartPublishingTimer() |
Start or restart the publishing timer and decrement the LifetimeCounter Variable. |
UpdateSubscriptionParams() |
Negotiate and update the Subscription parameters. If the new keep-alive interval is less than the current value of the keep-alive counter, perform ResetKeepAliveCounter() and ResetLifetimeCounter(). |