OPC UA客户端在断开/连接错误后从UA服务器捕获丢失的项目值?

时间:2018-10-15 11:25:02

标签: opc-ua

我正在使用OPC Foundation SDK构建OPC UA客户端。我可以创建一个包含一些Monitoreditems的订阅。

在OPC UA服务器上,这些受监视的项目会不断更改值(每秒左右)。

我想断开客户端连接(模拟连接断开),使订阅保持活动状态,然后等待一段时间。然后,我重新连接以恢复订阅,但是我也希望所有断开连接期间受监视的Item值排队。现在,我只能获得重新连接时的最后一个服务器值。

我正在设置队列大小:

monitoredItem.QueueSize = 100;

为了模拟连接错误,我在ClosesSession上将“删除订阅”设置为false: m_session.CloseSession(new RequestHeader(),false);

我的问题是断开/连接错误后如何捕获队列的内容?

客户端重新连接后,“丢失的值”是否应为“新的MonitoredItem_Notification”?

SubscriptionId是否应与断开连接之前相同?

sessionId应该相同还是新的SessionId让med保留现有订阅?模拟连接错误的最佳方法是什么?

许多问题:-)

我在其中创建包含一些MonitoredItems和MonitoredItem_Notification事件方法的订阅的代码示例。

外面有OPC UA大师吗?

if (node.Displayname == "node to monitor")
{
   MonitoredItem mon = CreateMonitoredItem((NodeId)node.reference.NodeId, node.Displayname);
   m_subscription.AddItem(mon);
   m_subscription.ApplyChanges();
}
private MonitoredItem CreateMonitoredItem(NodeId nodeId, string displayName)
{
    if (m_subscription == null)
    {

        m_subscription = new Subscription(m_session.DefaultSubscription);

        m_subscription.PublishingEnabled = true;
        m_subscription.PublishingInterval = 3000;//1000;
        m_subscription.KeepAliveCount = 10;
        m_subscription.LifetimeCount = 10;
        m_subscription.MaxNotificationsPerPublish = 1000;
        m_subscription.Priority = 100;
        bool cache = m_subscription.DisableMonitoredItemCache;

        m_session.AddSubscription(m_subscription);

        m_subscription.Create();
    }

        // add the new monitored item.
        MonitoredItem monitoredItem = new MonitoredItem(m_subscription.DefaultItem);
        //Each time a monitored item is sampled, the server evaluates the sample using a filter defined for each monitoreditem.
        //The server uses the filter to determine if the sample should be reported. The type of filter is dependent on the type of item.
        //DataChangeFilter for Variable, Eventfilter when monitoring Events. etc
        //MonitoringFilter f = new MonitoringFilter();
        //DataChangeFilter f = new DataChangeFilter();
        //f.DeadbandValue

        monitoredItem.StartNodeId = nodeId;
        monitoredItem.AttributeId = Attributes.Value;
        monitoredItem.DisplayName = displayName;
        //Disabled, Sampling, (Report (includes sampling))
        monitoredItem.MonitoringMode = MonitoringMode.Reporting;
        //How often the Client wish to check for new values on the server. Must be 0 if item is an event.
        //If a negative number the SamplingInterval is set equal to the PublishingInterval (inherited)
        //The Subscriptions KeepAliveCount should always be longer than the SamplingInterval/PublishingInterval
        monitoredItem.SamplingInterval = 500;
        //Number of samples stored on the server between each reporting
        monitoredItem.QueueSize = 100;
        monitoredItem.DiscardOldest = true;//Discard oldest values when full
        monitoredItem.CacheQueueSize = 100;

        monitoredItem.Notification += m_MonitoredItem_Notification;

        if (ServiceResult.IsBad(monitoredItem.Status.Error))
        {
            return null;
        }

        return monitoredItem;
    }



    private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke(new MonitoredItemNotificationEventHandler(MonitoredItem_Notification), monitoredItem, e);
            return;
        }

    try
        {
            if (m_session == null)
            {
                return;
            }

        MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;

        if (notification == null)
        {
            return;
        }


            string sess = m_session.SessionId.Identifier.ToString();
            string s = string.Format(" MonitoredItem: {0}\t Value: {1}\t Status: {2}\t SourceTimeStamp: {3}", monitoredItem.DisplayName, (notification.Value.WrappedValue.ToString().Length == 1) ? notification.Value.WrappedValue.ToString()  : notification.Value.WrappedValue.ToString(), notification.Value.StatusCode.ToString(), notification.Value.SourceTimestamp.ToLocalTime().ToString("HH:mm:ss.fff"));
            richTextBox1.AppendText(s + "SessionId: " + sess);
        }
        catch (Exception exception)
        {
            ClientUtils.HandleException(this.Text, exception);
        }
    }e here

1 个答案:

答案 0 :(得分:0)

我不知道您使用的SDK能为您提供多少,但是重新连接的方法通常是:

  1. 尝试恢复(重新激活)您的旧会话。如果成功完成,则您的订阅将已经存在,您需要做的就是发送更多的PublishRequests。由于您尝试通过关闭会话进行测试,因此这可能无法正常工作。

  2. 创建一个新会话,然后调用TransferSubscription服务将以前的订阅转移到您的新会话中。然后,您可以开始发送PublishRequests,您将获得排队的通知。

同样,根据您使用的堆栈/ SDK /工具包的不同,可能不会为您处理其中的某些情况。