BACnet的订阅 (七)

8 篇文章 1 订阅


现在有一个需求就是我只关注温度的变化,当BACnet设备(Bacnet.Room.Simulator以这个为例)的温度有变化时就直接推送给我,而不是我一直在实时监控这个温度值,如何实现呢? 答案就是预订“属性值改变”,请求当BACnet设备的温度对象的温度属性值改变时,要告知我。

订阅温度变化的效果图:
请添加图片描述

在这里插入图片描述
继续完善上一篇的功能,后面会给出整个工程的代码。

订阅属性值变化

发送一个订阅请求SubscribeCOVRequest,订阅某个对象的属性值变化。

        uint m_next_subscription_id = 0;
        /// <summary>
        /// 订阅
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSubscribe_Click(object sender, EventArgs e)
        {
            if (treeProperty.SelectedNode == null
               || treeDevice.SelectedNode == null)
                return;

            BacnetAddress adr = (BacnetAddress)treeDevice.SelectedNode.Tag;
            if (adr == null) return;

            BacnetObjectId objectId = (BacnetObjectId)treeProperty.SelectedNode.Tag;

            if (objectId == null) return;
            m_next_subscription_id++;

            try
            {
                if (RWServices.m_service.SubscribeCOVRequest(adr, objectId, m_next_subscription_id,
                    false, false, 0))
                {

                    ShowText(objectId.ToString() + "订阅成功!");
                }
            }catch(Exception ex)
            {
                ShowText(objectId.ToString() + "订阅失败: " + ex.Message);
            }
        }

值变化的通知

在发现设备时,我们就注册值变化通知的回调事件了。
在这里插入图片描述

        /// <summary>
        ///  ClientB属性值改变的通知
        /// </summary>
        private void Sender_OnCOVNotification(BacnetClient sender, BacnetAddress adr,
            byte invokeId, uint subscriberProcessIdentifier, 
            BacnetObjectId initiatingDeviceIdentifier,
            BacnetObjectId monitoredObjectIdentifier, 
            uint timeRemaining, bool needConfirm, 
            ICollection<BacnetPropertyValue> values, 
            BacnetMaxSegments maxSegments)
        {
            foreach (BacnetPropertyValue value in values)
            {
                switch ((BacnetPropertyIds)value.property.propertyIdentifier)
                {
                    case BacnetPropertyIds.PROP_PRESENT_VALUE:
                        
                        try
                        {
                            if(value.value.Count > 0)
                            {
                                ShowText("OnCOVNotification : " + value.value[0].Value);
                            }
                          
                        }
                        catch { }
                        break;
                    case BacnetPropertyIds.PROP_STATUS_FLAGS:
                        if (value.value != null && value.value.Count > 0)
                        {
                            BacnetStatusFlags status = (BacnetStatusFlags)((BacnetBitString)value.value[0].Value).ConvertToInt();
                            string status_text = "";
                            if ((status & BacnetStatusFlags.STATUS_FLAG_FAULT) == BacnetStatusFlags.STATUS_FLAG_FAULT)
                                status_text += "FAULT,";
                            else if ((status & BacnetStatusFlags.STATUS_FLAG_IN_ALARM) == BacnetStatusFlags.STATUS_FLAG_IN_ALARM)
                                status_text += "ALARM,";
                            else if ((status & BacnetStatusFlags.STATUS_FLAG_OUT_OF_SERVICE) == BacnetStatusFlags.STATUS_FLAG_OUT_OF_SERVICE)
                                status_text += "OOS,";
                            else if ((status & BacnetStatusFlags.STATUS_FLAG_OVERRIDDEN) == BacnetStatusFlags.STATUS_FLAG_OVERRIDDEN)
                                status_text += "OR,";
                            if (status_text != "")
                            {
                                status_text = status_text.Substring(0, status_text.Length - 1);
                                ShowText("OnCOVNotification : " + status_text);
                            }
                            else
                                ShowText("OnCOVNotification :  OK");
                           
                        }

                        break;
                    default:
                        //got something else? ignore it
                        break;
                }
            }
        }

对方是怎么推送值变化的

当BACnet的某个对象的某个属性值被订阅时,它自己也会在OnSubscribeCOV回调中收到这个属性被订阅的消息,在这个回调中,它先记录下来这个订阅,并发送SimpleAckResponse告诉订阅方:你已经订阅这个属性值变化成功了,随后马上发这个属性值过去。(这里主要是将被订阅的属性值记录下来)

接下来在属性值发生变化时,在ChangeOfValue中将订阅属性值发生变化的数值发送,即主动推送属性值变化。

下面的这段代码是Bacnet.Room.Simulator中源码的一部分,感兴趣的可以去看看,最好手动敲一遍这个demo。

   //触发订阅
  private static void OnSubscribeCOV(BacnetClient sender, BacnetAddress adr, byte invoke_id, uint subscriberProcessIdentifier, BacnetObjectId monitoredObjectIdentifier, bool cancellationRequest, bool issueConfirmedNotifications, uint lifetime, BacnetMaxSegments max_segments)
        {
            
            lock (m_lockObject)
            {
                try
                {
                    //create  创建一个订阅
                    Subscription sub = HandleSubscriptionRequest(sender, adr, invoke_id,
                        subscriberProcessIdentifier, monitoredObjectIdentifier,
                        (uint)BacnetPropertyIds.PROP_ALL, 
                        cancellationRequest, 
                        issueConfirmedNotifications, lifetime, 0);

                    //send confirm  发送Ack证实服务
                    sender.SimpleAckResponse(adr,
                        BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV,
                        invoke_id);

                    //also send first values 发送属性值
                    if (!cancellationRequest)
                    {
                        System.Threading.ThreadPool.QueueUserWorkItem((o) =>
                                {

                                    IList<BacnetPropertyValue> values;
                                    if(m_storage.ReadPropertyAll(sub.monitoredObjectIdentifier, out values))
                                        if (!sender.Notify(adr, sub.subscriberProcessIdentifier, 
                                            m_storage.DeviceId, sub.monitoredObjectIdentifier,
                                            (uint)sub.GetTimeRemaining(), 
                                            sub.issueConfirmedNotifications, values))
                                            Trace.TraceError("Couldn't send notify");
                                }, null);
                    }
                }
                catch (Exception)
                {
                    sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV, invoke_id,
                        BacnetErrorClasses.ERROR_CLASS_DEVICE, 
                        BacnetErrorCodes.ERROR_CODE_OTHER);
                }
            }
        }


//值改变
  private static void m_storage_ChangeOfValue(DeviceStorage sender, BacnetObjectId object_id, BacnetPropertyIds property_id, uint array_index, IList<BacnetValue> value)
        {
            System.Threading.ThreadPool.QueueUserWorkItem((o) =>
            {
                lock (m_lockObject)
                {
                    //remove old leftovers  移除过期的订阅
                    RemoveOldSubscriptions();

                    //find subscription  找订阅
                    if (!m_subscriptions.ContainsKey(object_id)) return;
                    List<Subscription> subs = m_subscriptions[object_id];

                    //convert
                    List<BacnetPropertyValue> values = new List<BacnetPropertyValue>();
                    BacnetPropertyValue tmp = new BacnetPropertyValue();
                    tmp.property = new BacnetPropertyReference((uint)property_id, array_index);
                    tmp.value = value;
                    values.Add(tmp);

                    //send to all  遍历所有的订阅
                    foreach (Subscription sub in subs)
                    {
                        if (sub.monitoredProperty.propertyIdentifier == (uint)BacnetPropertyIds.PROP_ALL || sub.monitoredProperty.propertyIdentifier == (uint)property_id)
                        {
                            //send notify  发送通知
                            if (!sub.reciever.Notify(sub.reciever_address, sub.subscriberProcessIdentifier, m_storage.DeviceId, sub.monitoredObjectIdentifier, (uint)sub.GetTimeRemaining(), sub.issueConfirmedNotifications, values))
                                Trace.TraceError("Couldn't send notify");
                        }
                    }
                }
            }, null);
        }


订阅属性值变化小结

订阅:总之就是一个去订阅属性值变化,另一个接受订阅并返回ACK,
值变化: 一个主动发送属性值变化,另外一个接收到值变化的通知。
两个设备之间有来有往。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值