模拟BACnet设备(八)

文章目录

    • 前言
    • 模拟呼梯设备的功能
    • 前期准备——xml文件的编写
    • 创建工程,建立BACnet模拟设备
    • 如何将设备的对象列表打包发送呢?
    • 被订阅的属性值变化时,如何主动通知对方?
    • 读写属性值
    • 完整代码
    • 小结

前言

前面一到七篇,从理论,工具到实践介绍了BACnet,今天这一篇我们来模拟一下BACnet设备。
我们模拟Bacnet.Room.Simulator来写一个有关于电梯呼梯的BACnet设备.
完整工程代码下载地址:https://download.csdn.net/download/weixin_40314351/89161436
效果图如下:
请添加图片描述

在这里插入图片描述

模拟呼梯设备的功能

这个呼梯设备呢,比较简单只有三个参数,最低楼层,最高楼层和当前楼层,会变化的是当前楼层,随着上行或下行的时候当前楼层就会变化,下面是这个Demo的UI。

在这里插入图片描述
实现的功能有:
1 能在Yabe中查找到这个设备,以及显示这个设备的所有对象;
2 实现基本的读写功能;
3 能够实现订阅属性值变化的功能;

前期准备——xml文件的编写

作为一个BACnet模拟设备,需要有一个xml文件,按面向对象的结构记录设备的对象以及属性和值。参考Bacnet.Room.Simulator的DeviceStorage.Xml,自己也写了一个模拟电梯的DeviceStorage.Xml,代码如下:
注意不要缺少或过多一些 < > 等字符,否则在加载的时候会报错,报错了也不是什么大问题,它会提醒哪一行第几列有错误,改正就可以了。

<?xml version="1.0"?>
<DeviceStorage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Objects><!--设备对象--><Object Type="OBJECT_DEVICE" Instance="64237"><Properties><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_DEVICE:64237</Value></Property><Property Id="PROP_OBJECT_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>ElevatorBacnet</Value></Property><Property Id="PROP_OBJECT_TYPE" TAG="BACNET_APPLICATION_TAG_ENUMERATED"><Value>8</Value></Property><Property Id="PROP_SYSTEM_STATUS" TAG="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_VENDOR_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>F. Chaxel,Thanks to Morten Kvistgaard,MIT license,2015</Value></Property><Property Id="PROP_VENDOR_IDENTIFIER" TAG="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>61440</Value></Property><Property Id="PROP_MODEL_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Hpmont_FC_2024</Value></Property><Property Id="PROP_FIRMWARE_REVISION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>0.1.0</Value></Property><Property Id="PROP_APPLICATION_SOFTWARE_VERSION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>0.1.0</Value></Property><Property Id="PROP_PROTOCOL_VERSION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1</Value></Property><Property Id="PROP_PROTOCOL_REVISION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>14</Value></Property><Property Id="PROP_PROTOCOL_SERVICES_SUPPORTED" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>01111111101111000011101110000000011010101</Value></Property><Property Id="PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000000010101010000000000000000100000000111110111111111</Value></Property><Property Id="PROP_OBJECT_LIST" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"></Property><Property Id="PROP_MAX_APDU_LENGTH_ACCEPTED" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1476</Value></Property><Property Id="PROP_SEGMENTATION_SUPPORTED" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>3</Value></Property><Property Id="PROP_APDU_TIMEOUT" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3000</Value></Property><Property Id="PROP_NUMBER_OF_APDU_RETRIES" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3</Value></Property><Property Id="PROP_DEVICE_ADDRESS_BINDING" Tag="BACNET_APPLICATION_TAG_NULL"/><Property Id="PROP_DATABASE_REVISION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>0</Value></Property><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Free ElevatorController Simulator, X,xm, 2024</Value></Property><Property Id="PROP_LOCATION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>China.Shenzhen</Value></Property></Properties></Object><!--模拟输入 0--><Object Type="OBJECT_ANALOG_INPUT" Instance="0"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Elevator Min Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_INPUT:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Min.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value><!--UNITS_NO_UNITS--></Property></Properties></Object><!--模拟输入 1--><Object Type="OBJECT_ANALOG_INPUT" Instance="1"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Elevator Max Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_INPUT:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Max.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>9</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value><!--UNITS_NO_UNITS--></Property></Properties></Object><!--模拟值--><Object Type="OBJECT_ANALOG_VALUE" Instance="0"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Current Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_VALUE:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Current.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>2</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value></Property></Properties></Object></Objects>
</DeviceStorage>

创建工程,建立BACnet模拟设备

使用VS2019创建一个.NET Framework 4.6.1的桌面应用应用程序,NuGet管理包下载BACnet.dll到项目中。

在这里插入图片描述

创建一个BacnetActivity.cs文件类,用于对BACnet设备的管理。
因为是作为一个BACnet模拟设备,需要实现的功能肯定有:
被发现: OnWhoIs
被访问读写: OnReadPropertyRequest OnWritePropertyRequest
被订阅属性值变化: OnSubscribeCOV

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.NetworkInformation;using System.IO.BACnet;
using System.IO.BACnet.Storage;
using System.Windows.Forms;
using System.Diagnostics;namespace ElevatorBacnet
{static  class BacnetActivity{public static DeviceStorage m_storage;public static string m_local_ip_endpoint = "192.168.2.164";public static BacnetClient m_ip_server;private static Dictionary<BacnetObjectId, List<Subscription>> m_subscriptions= new Dictionary<BacnetObjectId, List<Subscription>>();public static object m_lockObject = new object();private static BacnetSegmentations m_supported_segentation =BacnetSegmentations.SEGMENTATION_BOTH;internal static uint deviceId = 1234;//重新初始化public static void ReInitialize(){try{//initif (m_ip_server != null)m_ip_server.Dispose();PhysicalAddress macAddr = null;var netiface = NetworkInterface.GetAllNetworkInterfaces();foreach(NetworkInterface net in netiface){if(net.OperationalStatus == OperationalStatus.Up&& net.NetworkInterfaceType == NetworkInterfaceType.Ethernet){macAddr = net.GetPhysicalAddress(); break;}}/*  PhysicalAddress physical = (from netface in NetworkInterface.GetAllNetworkInterfaces()where ((netface.OperationalStatus == OperationalStatus.Up) &&(netface.NetworkInterfaceType == NetworkInterfaceType.Ethernet))select netface.GetPhysicalAddress()).FirstOrDefault();*/if (Program.DeviceId == -1){if (macAddr != null){byte[] mac = macAddr.GetAddressBytes();deviceId = (uint)mac[5] + (uint)((mac[4] << 8) << 6);}deviceId = deviceId + ((uint)Program.Count & 0x3F);}elsedeviceId = (uint)Program.DeviceId;Program.DeviceId = (int)deviceId;m_storage = DeviceStorage.Load("DeviceStorage.xml", deviceId);m_storage.ReadOverride += ReadOverride;m_storage.ChangeOfValue += ChangeOfValue;//create udp service pointBacnetIpUdpProtocolTransport udp = newBacnetIpUdpProtocolTransport(port: 0xBAC0, useExclusivePort: false,localEndpointIp: m_local_ip_endpoint);m_ip_server = new BacnetClient(udp);m_ip_server.OnWhoIs += OnWhoIs;m_ip_server.OnReadPropertyRequest += OnReadPropertyRequest;m_ip_server.OnWritePropertyRequest += OnWritePropertyRequest;m_ip_server.OnReadPropertyMultipleRequest += OnReadPropertyMultipleRequest;m_ip_server.OnSubscribeCOV += OnSubscribeCOV;m_ip_server.OnSubscribeCOVProperty += OnSubscribeCOVProperty;m_ip_server.Start();//发送问候m_ip_server.Iam(m_storage.DeviceId, m_supported_segentation);}catch(Exception ex){MessageBox.Show(ex.Message);}}}

如何将设备的对象列表打包发送呢?

Yabe能够获取BACnet设备的所有对象列表,那么BACnet设备是怎么将它自己的对象列表整合发送的?答案是在DeviceStorage类的回调方法ReadOverride中实现的。在这个重写方法中,如果对方请求读取自己的对象列表就会在这个方法中将对象列表打包。如下:

当然这个方法里面还包括一些其它的,比如说明支持的对象类型, 支持的标准协议服务,以及是否支持分段等。
为什么要用回调方法ReadOverride,而不是直接从配置文件xml中读取,因为xml是明文可能会被其他人恶意更改,导致程序不能正常运行。

private static void ReadOverride(BacnetObjectId objectId, BacnetPropertyIds propertyId, uint arrayIndex, out IList<BacnetValue> value, out DeviceStorage.ErrorCodes status, out bool handled){handled = true;value = new BacnetValue[0];status = DeviceStorage.ErrorCodes.Good;if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&propertyId == BacnetPropertyIds.PROP_OBJECT_LIST){//对象列表 列出设备中的可被BACnet 服务访问的所有对象的标识符if (arrayIndex == 0){//对象列表value = new BacnetValue[]{new BacnetValue(BacnetApplicationTags.BACNET_APPLICATION_TAG_UNSIGNED_INT,(uint)m_storage.Objects.Length)};}else if(arrayIndex != System.IO.BACnet.Serialize.ASN1.BACNET_ARRAY_ALL){//object list indexvalue = new BacnetValue[]{new BacnetValue(BacnetApplicationTags.BACNET_APPLICATION_TAG_OBJECT_ID,new BacnetObjectId(m_storage.Objects[arrayIndex-1].Type,m_storage.Objects[arrayIndex-1].Instance))};}else{//整个对象列表 object list wholeBacnetValue[] list = new BacnetValue[m_storage.Objects.Length];for(int i=0; i<list.Length;i++){list[i].Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_OBJECT_ID;list[i].Value = new BacnetObjectId(m_storage.Objects[i].Type,m_storage.Objects[i].Instance);}value = list;}}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED){//协议对象类型支持 表示设备的协议实现所支持的标准对象类型BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_BIT_STRING;BacnetBitString b = new BacnetBitString();//全部设置为false set all falseb.SetBit((byte)BacnetObjectTypes.MAX_ASHRAE_OBJECT_TYPE, false);b.SetBit((byte)BacnetObjectTypes.OBJECT_ANALOG_INPUT, true);b.SetBit((byte)BacnetObjectTypes.OBJECT_DEVICE, true);b.SetBit((byte)BacnetObjectTypes.OBJECT_ANALOG_VALUE, true);v.Value = b;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_PROTOCOL_SERVICES_SUPPORTED ){//协议服务支持 表示设备的协议实现所支持的标准协议服务BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_BIT_STRING;BacnetBitString b = new BacnetBitString();b.SetBit((byte)BacnetServicesSupported.MAX_BACNET_SERVICES_SUPPORTED, false);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_I_AM, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_WHO_IS, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_READ_PROP_MULTIPLE, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_READ_PROPERTY, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_WRITE_PROPERTY, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_SUBSCRIBE_COV, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY, true);v.Value = b;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_SEGMENTATION_SUPPORTED){//分段支持 表示设备是否支持报文分段,分段传输和分段接收BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_ENUMERATED;v.Value = (uint)BacnetSegmentations.SEGMENTATION_BOTH;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_SYSTEM_STATUS){//系统状态 表示设备的物理和逻辑状态BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_ENUMERATED;//可以是任何modelv.Value = (uint)BacnetDeviceStatus.OPERATIONAL;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_ACTIVE_COV_SUBSCRIPTIONS){//暂未实现handled = false;}else if(objectId.type == BacnetObjectTypes.OBJECT_OCTETSTRING_VALUE &&objectId.instance == 0 &&propertyId == BacnetPropertyIds.PROP_PRESENT_VALUE){//暂未实现handled = false;}else if(objectId.type == BacnetObjectTypes.OBJECT_GROUP &&propertyId == BacnetPropertyIds.PROP_PRESENT_VALUE){//对象组//暂时未实现handled = false;}else{handled = false;}}

从源码DeviceStorage类中可以看到在执行读取属性ReadProperty的时候会判断ReadOverride回调方法是否为空,如果不为空则会先执行ReadOverride且当这个方法的返回值为true时,就直接返回回调结果了,而不会继续往下走。如果返回值是false,则会继续往下走下面的逻辑。
在这里插入图片描述

被订阅的属性值变化时,如何主动通知对方?

前面的提及到的OnSubscribeCOV只是将被订阅的属性记录起来,并发送一个ACK给对方而已,而当订阅值发生变化时是在DeviceStorage类的ChangeOfValue回调方法中主动通知对方的。

  private static void ChangeOfValue(DeviceStorage sender, BacnetObjectId object_id, BacnetPropertyIds property_id, uint array_index, IList<BacnetValue> value){System.Threading.ThreadPool.QueueUserWorkItem((o) =>{lock (m_lockObject){Console.WriteLine("Enter ChangeOfValue");//remove old lefto versRemoveOldSubscriptions();//find subscriptionif (!m_subscriptions.ContainsKey(object_id)) return;List<Subscription> subs = m_subscriptions[object_id];//convertList<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 allforeach (Subscription sub in subs){if (sub.monitoredProperty.propertyIdentifier == (uint)BacnetPropertyIds.PROP_ALL || sub.monitoredProperty.propertyIdentifier == (uint)property_id){//send notifyif (!sub.reciever.Notify(sub.reciever_address, sub.subscriberProcessIdentifier,m_storage.DeviceId, sub.monitoredObjectIdentifer, (uint)sub.GetTimeRemaining(), sub.issueConfimedNotifications, values))Trace.TraceError("Couldn't send notify");}}}}, null);}

读写属性值

OnWritePropertyRequest收到被写的请求后要先判断这个属性值是否允许写入,以及这个属性值是否在xml文件中,找得到属性且被允许写入才能写入,否则就回一个Err.

OnReadPropertyRequest读请求也是类似的,先判断这个属性值是否在xml文件中,如果存在则返回当前值给请求方,若不存在则返回Err.

         /// <summary>///响应写的请求/// </summary>private static void OnWritePropertyRequest(BacnetClient sender, BacnetAddress adr,byte invokeId, BacnetObjectId objectId, BacnetPropertyValue value,BacnetMaxSegments maxSegments){//先判断这个对象的属性是否支持写入BacnetPropertyIds PropId = (BacnetPropertyIds)value.property.propertyIdentifier;bool AllowWrite =(objectId.Equals("OBJECT_ANALOG_VALUE:0") && (PropId == BacnetPropertyIds.PROP_OUT_OF_SERVICE)) ||(objectId.Equals("OBJECT_ANALOG_VALUE:0") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE)) ||(objectId.Equals("OBJECT_ANALOG_INPUT:0") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE)) ||(objectId.Equals("OBJECT_ANALOG_INPUT:1") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE));if (AllowWrite == false){sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_WRITE_ACCESS_DENIED);return;}lock (m_lockObject){try{//先写配置xml文件中这个对象DeviceStorage.ErrorCodes code = m_storage.WriteCommandableProperty(objectId,(BacnetPropertyIds)value.property.propertyIdentifier,value.value[0], value.priority);if (code == DeviceStorage.ErrorCodes.NotForMe)code = m_storage.WriteProperty(objectId,(BacnetPropertyIds)value.property.propertyIdentifier,value.property.propertyArrayIndex,value.value);//回应写的请求if (code == DeviceStorage.ErrorCodes.Good){sender.SimpleAckResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId);}else{if(code == DeviceStorage.ErrorCodes.WriteAccessDenied){sender.ErrorResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_WRITE_ACCESS_DENIED);}elsesender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}catch{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}/// <summary>///响应读的请求/// </summary>private static void OnReadPropertyRequest(BacnetClient sender, BacnetAddress adr, byte invokeId, BacnetObjectId objectId, BacnetPropertyReference property, BacnetMaxSegments maxSegments){lock (m_lockObject){try{//先看看配置xml文件中是否有这个对象IList<BacnetValue> value;DeviceStorage.ErrorCodes code = m_storage.ReadProperty(objectId, property.GetPropertyId(),property.propertyArrayIndex,out value);if (code == DeviceStorage.ErrorCodes.Good){//回应读的请求sender.ReadPropertyResponse(adr, invokeId,sender.GetSegmentBuffer(maxSegments),objectId, property, value);}else{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}catch{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}

完整代码

1 BacnetActivity.cs文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.NetworkInformation;using System.IO.BACnet;
using System.IO.BACnet.Storage;
using System.Windows.Forms;
using System.Diagnostics;namespace ElevatorBacnet
{static  class BacnetActivity{public static DeviceStorage m_storage;public static string m_local_ip_endpoint = "192.168.2.164";public static BacnetClient m_ip_server;private static Dictionary<BacnetObjectId, List<Subscription>> m_subscriptions= new Dictionary<BacnetObjectId, List<Subscription>>();public static object m_lockObject = new object();private static BacnetSegmentations m_supported_segentation =BacnetSegmentations.SEGMENTATION_BOTH;internal static uint deviceId = 1234;//重新初始化public static void ReInitialize(){try{//initif (m_ip_server != null)m_ip_server.Dispose();PhysicalAddress macAddr = null;var netiface = NetworkInterface.GetAllNetworkInterfaces();foreach(NetworkInterface net in netiface){if(net.OperationalStatus == OperationalStatus.Up&& net.NetworkInterfaceType == NetworkInterfaceType.Ethernet){macAddr = net.GetPhysicalAddress(); break;}}/*  PhysicalAddress physical = (from netface in NetworkInterface.GetAllNetworkInterfaces()where ((netface.OperationalStatus == OperationalStatus.Up) &&(netface.NetworkInterfaceType == NetworkInterfaceType.Ethernet))select netface.GetPhysicalAddress()).FirstOrDefault();*/if (Program.DeviceId == -1){if (macAddr != null){byte[] mac = macAddr.GetAddressBytes();deviceId = (uint)mac[5] + (uint)((mac[4] << 8) << 6);}deviceId = deviceId + ((uint)Program.Count & 0x3F);}elsedeviceId = (uint)Program.DeviceId;Program.DeviceId = (int)deviceId;m_storage = DeviceStorage.Load("DeviceStorage.xml", deviceId);m_storage.ReadOverride += ReadOverride;m_storage.ChangeOfValue += ChangeOfValue;//create udp service pointBacnetIpUdpProtocolTransport udp = newBacnetIpUdpProtocolTransport(port: 0xBAC0, useExclusivePort: false,localEndpointIp: m_local_ip_endpoint);m_ip_server = new BacnetClient(udp);m_ip_server.OnWhoIs += OnWhoIs;m_ip_server.OnReadPropertyRequest += OnReadPropertyRequest;m_ip_server.OnWritePropertyRequest += OnWritePropertyRequest;m_ip_server.OnReadPropertyMultipleRequest += OnReadPropertyMultipleRequest;m_ip_server.OnSubscribeCOV += OnSubscribeCOV;m_ip_server.OnSubscribeCOVProperty += OnSubscribeCOVProperty;m_ip_server.Start();//发送问候m_ip_server.Iam(m_storage.DeviceId, m_supported_segentation);}catch(Exception ex){MessageBox.Show(ex.Message);}}#region xml的读写public static BacnetValue GetBacnetPresentValue(BacnetObjectId objectId){lock (m_lockObject){IList<BacnetValue> val = null;m_storage.ReadProperty(objectId,BacnetPropertyIds.PROP_PRESENT_VALUE, 1,out val);return val[0];}}public static void SetBacnetPresentValue(BacnetObjectId id, BacnetValue bv){if (GetBacnetPresentValue(id).Value.ToString() == bv.Value.ToString())return;lock(m_lockObject){IList<BacnetValue> val = new BacnetValue[1] { bv };m_storage.WriteProperty(id, BacnetPropertyIds.PROP_PRESENT_VALUE,1, val, true);}}#endregionprivate static void ReadOverride(BacnetObjectId objectId, BacnetPropertyIds propertyId, uint arrayIndex, out IList<BacnetValue> value, out DeviceStorage.ErrorCodes status, out bool handled){handled = true;value = new BacnetValue[0];status = DeviceStorage.ErrorCodes.Good;if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&propertyId == BacnetPropertyIds.PROP_OBJECT_LIST){//对象列表 列出设备中的可被BACnet 服务访问的所有对象的标识符if (arrayIndex == 0){//对象列表value = new BacnetValue[]{new BacnetValue(BacnetApplicationTags.BACNET_APPLICATION_TAG_UNSIGNED_INT,(uint)m_storage.Objects.Length)};}else if(arrayIndex != System.IO.BACnet.Serialize.ASN1.BACNET_ARRAY_ALL){//object list indexvalue = new BacnetValue[]{new BacnetValue(BacnetApplicationTags.BACNET_APPLICATION_TAG_OBJECT_ID,new BacnetObjectId(m_storage.Objects[arrayIndex-1].Type,m_storage.Objects[arrayIndex-1].Instance))};}else{//整个对象列表 object list wholeBacnetValue[] list = new BacnetValue[m_storage.Objects.Length];for(int i=0; i<list.Length;i++){list[i].Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_OBJECT_ID;list[i].Value = new BacnetObjectId(m_storage.Objects[i].Type,m_storage.Objects[i].Instance);}value = list;}}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED){//协议对象类型支持 表示设备的协议实现所支持的标准对象类型BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_BIT_STRING;BacnetBitString b = new BacnetBitString();//全部设置为false set all falseb.SetBit((byte)BacnetObjectTypes.MAX_ASHRAE_OBJECT_TYPE, false);b.SetBit((byte)BacnetObjectTypes.OBJECT_ANALOG_INPUT, true);b.SetBit((byte)BacnetObjectTypes.OBJECT_DEVICE, true);b.SetBit((byte)BacnetObjectTypes.OBJECT_ANALOG_VALUE, true);v.Value = b;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_PROTOCOL_SERVICES_SUPPORTED ){//协议服务支持 表示设备的协议实现所支持的标准协议服务BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_BIT_STRING;BacnetBitString b = new BacnetBitString();b.SetBit((byte)BacnetServicesSupported.MAX_BACNET_SERVICES_SUPPORTED, false);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_I_AM, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_WHO_IS, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_READ_PROP_MULTIPLE, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_READ_PROPERTY, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_WRITE_PROPERTY, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_SUBSCRIBE_COV, true);b.SetBit((byte)BacnetServicesSupported.SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY, true);v.Value = b;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_SEGMENTATION_SUPPORTED){//分段支持 表示设备是否支持报文分段,分段传输和分段接收BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_ENUMERATED;v.Value = (uint)BacnetSegmentations.SEGMENTATION_BOTH;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_SYSTEM_STATUS){//系统状态 表示设备的物理和逻辑状态BacnetValue v = new BacnetValue();v.Tag = BacnetApplicationTags.BACNET_APPLICATION_TAG_ENUMERATED;//可以是任何modelv.Value = (uint)BacnetDeviceStatus.OPERATIONAL;value = new BacnetValue[] { v };}else if(objectId.type == BacnetObjectTypes.OBJECT_DEVICE &&objectId.instance == m_storage.DeviceId &&propertyId == BacnetPropertyIds.PROP_ACTIVE_COV_SUBSCRIPTIONS){//暂未实现handled = false;}else if(objectId.type == BacnetObjectTypes.OBJECT_OCTETSTRING_VALUE &&objectId.instance == 0 &&propertyId == BacnetPropertyIds.PROP_PRESENT_VALUE){//暂未实现handled = false;}else if(objectId.type == BacnetObjectTypes.OBJECT_GROUP &&propertyId == BacnetPropertyIds.PROP_PRESENT_VALUE){//对象组//暂时未实现handled = false;}else{handled = false;}}private static void ChangeOfValue(DeviceStorage sender, BacnetObjectId object_id, BacnetPropertyIds property_id, uint array_index, IList<BacnetValue> value){System.Threading.ThreadPool.QueueUserWorkItem((o) =>{lock (m_lockObject){Console.WriteLine("Enter ChangeOfValue");//remove old lefto versRemoveOldSubscriptions();//find subscriptionif (!m_subscriptions.ContainsKey(object_id)) return;List<Subscription> subs = m_subscriptions[object_id];//convertList<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 allforeach (Subscription sub in subs){if (sub.monitoredProperty.propertyIdentifier == (uint)BacnetPropertyIds.PROP_ALL || sub.monitoredProperty.propertyIdentifier == (uint)property_id){//send notifyif (!sub.reciever.Notify(sub.reciever_address, sub.subscriberProcessIdentifier,m_storage.DeviceId, sub.monitoredObjectIdentifer, (uint)sub.GetTimeRemaining(), sub.issueConfimedNotifications, values))Trace.TraceError("Couldn't send notify");}}}}, null);}/// <summary>/// 订阅描述/// </summary>private class Subscription{public BacnetClient reciever;public BacnetAddress reciever_address;public uint subscriberProcessIdentifier;public BacnetObjectId monitoredObjectIdentifer;public BacnetPropertyReference monitoredProperty;public bool issueConfimedNotifications;public uint lefttime;public DateTime start;public float covIncrement;public Subscription(BacnetClient reciever, BacnetAddress reciever_address,uint subscriberProcessIdentifier, BacnetObjectId monitoredObjectIdentifer,BacnetPropertyReference monitoredProperty,bool issueConfimedNotifications, uint lefttime,float covIncrement){this.reciever = reciever;this.reciever_address = reciever_address;this.subscriberProcessIdentifier = subscriberProcessIdentifier;this.monitoredObjectIdentifer = monitoredObjectIdentifer;this.monitoredProperty = monitoredProperty;this.issueConfimedNotifications = issueConfimedNotifications;this.lefttime = lefttime;this.start = DateTime.Now;this.covIncrement = covIncrement;}public int GetTimeRemaining(){if (lefttime == 0) return 0;uint elapse = (uint)(DateTime.Now - start).TotalSeconds;if(lefttime > elapse){return (int)(lefttime - elapse);}return -1;}}//移除旧的订阅private static void RemoveOldSubscriptions(){LinkedList<BacnetObjectId> to_be_deleted = new LinkedList<BacnetObjectId>();foreach(KeyValuePair<BacnetObjectId, List<Subscription>> entry in  m_subscriptions){for(int i=0; i<entry.Value.Count; i++){if(entry.Value[i].GetTimeRemaining() < 0){//移除订阅时间为0的属性entry.Value.RemoveAt(i);i--; }}if (entry.Value.Count == 0)to_be_deleted.AddLast(entry.Key);}foreach (BacnetObjectId obj_id in to_be_deleted)m_subscriptions.Remove(obj_id);}private static Subscription HandleSubscriptionRequest(BacnetClient sender, BacnetAddress adr,byte invokde_id,uint subscriberProcessIdentifier,BacnetObjectId monitoredObjectIdentifer,uint property_id, bool cancellationRequest,bool issueConfimedNotifications, uint lefttime,float covIncrement){//移除旧的剩余的订阅RemoveOldSubscriptions();//找存在的List<Subscription> subs = null;Subscription sub = null;if(m_subscriptions.ContainsKey(monitoredObjectIdentifer)){subs = m_subscriptions[monitoredObjectIdentifer];foreach(Subscription s in subs){if(s.reciever.Equals(sender) &&s.reciever_address.Equals(adr) &&s.monitoredObjectIdentifer.Equals(monitoredObjectIdentifer)&&s.monitoredProperty.propertyIdentifier == property_id){sub = s;break;}}}//取消订阅if(cancellationRequest && sub != null){subs.Remove(sub);if (subs.Count == 0)m_subscriptions.Remove(sub.monitoredObjectIdentifer);//发送确认sender.SimpleAckResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV,invokde_id);return null;}//如果需要则创建if(sub == null){sub = new Subscription(sender, adr, subscriberProcessIdentifier,monitoredObjectIdentifer, new BacnetPropertyReference((uint)BacnetPropertyIds.PROP_ALL,System.IO.BACnet.Serialize.ASN1.BACNET_ARRAY_ALL),issueConfimedNotifications, lefttime, covIncrement);if(subs == null){subs = new List<Subscription>();m_subscriptions.Add(sub.monitoredObjectIdentifer, subs);}subs.Add(sub);}//可能需要更新sub.issueConfimedNotifications = issueConfimedNotifications;sub.lefttime = lefttime;sub.start = DateTime.Now;return sub;}/// <summary>///响应订阅的请求/// </summary>private static void OnSubscribeCOV(BacnetClient sender, BacnetAddress adr, byte invokeId, uint subscriberProcessIdentifier, BacnetObjectId monitoredObjectIdentifier,bool cancellationRequest, bool issueConfirmedNotifications, uint lifetime, BacnetMaxSegments maxSegments){lock(m_lockObject){try{Console.WriteLine("Enter OnSubscribeCOV");//创建一个订阅Subscription sub = HandleSubscriptionRequest(sender, adr,invokeId, subscriberProcessIdentifier, monitoredObjectIdentifier,(uint)BacnetPropertyIds.PROP_ALL,cancellationRequest,issueConfirmedNotifications, lifetime, 0);//发送确认sender.SimpleAckResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV,invokeId);//且发送当前值if (!cancellationRequest){System.Threading.ThreadPool.QueueUserWorkItem((o) =>{ IList<BacnetPropertyValue> values;if (m_storage.ReadPropertyAll(sub.monitoredObjectIdentifer, out values)){if (!sender.Notify(adr, sub.subscriberProcessIdentifier,m_storage.DeviceId, sub.monitoredObjectIdentifer,(uint)sub.GetTimeRemaining(),sub.issueConfimedNotifications, values)){Trace.TraceError("Couldn't send notify");}}}, null);}}catch{sender.ErrorResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}private static void OnSubscribeCOVProperty(BacnetClient sender, BacnetAddress adr, byte invokeId, uint subscriberProcessIdentifier,BacnetObjectId monitoredObjectIdentifier,BacnetPropertyReference monitoredProperty, bool cancellationRequest, bool issueConfirmedNotifications,uint lifetime, float covIncrement, BacnetMaxSegments maxSegments){lock (m_lockObject){try {Console.WriteLine("Enter OnSubscribeCOVProperty");//crceateSubscription sub = HandleSubscriptionRequest(sender, adr,invokeId, subscriberProcessIdentifier,monitoredObjectIdentifier, (uint)BacnetPropertyIds.PROP_ALL,cancellationRequest, issueConfirmedNotifications, lifetime,covIncrement);//send confirmsender.SimpleAckResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY,invokeId);//also send first valuesif (!cancellationRequest){System.Threading.ThreadPool.QueueUserWorkItem((o) =>{IList<BacnetValue> _values;m_storage.ReadProperty(sub.monitoredObjectIdentifer,(BacnetPropertyIds)sub.monitoredProperty.propertyIdentifier,sub.monitoredProperty.propertyArrayIndex, out _values);List<BacnetPropertyValue> values = new List<BacnetPropertyValue>();BacnetPropertyValue tmp = new BacnetPropertyValue();tmp.property = sub.monitoredProperty;tmp.value = _values;values.Add(tmp);if (!sender.Notify(adr, sub.subscriberProcessIdentifier,m_storage.DeviceId, sub.monitoredObjectIdentifer,(uint)sub.GetTimeRemaining(),sub.issueConfimedNotifications, values)){Trace.TraceError("Couldn't send notify");}}, null);}}catch{sender.ErrorResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}/// <summary>///响应写的请求/// </summary>private static void OnWritePropertyRequest(BacnetClient sender, BacnetAddress adr,byte invokeId, BacnetObjectId objectId, BacnetPropertyValue value,BacnetMaxSegments maxSegments){//先判断这个对象的属性是否支持写入BacnetPropertyIds PropId = (BacnetPropertyIds)value.property.propertyIdentifier;bool AllowWrite =(objectId.Equals("OBJECT_ANALOG_VALUE:0") && (PropId == BacnetPropertyIds.PROP_OUT_OF_SERVICE)) ||(objectId.Equals("OBJECT_ANALOG_VALUE:0") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE)) ||(objectId.Equals("OBJECT_ANALOG_INPUT:0") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE)) ||(objectId.Equals("OBJECT_ANALOG_INPUT:1") && (PropId == BacnetPropertyIds.PROP_PRESENT_VALUE));if (AllowWrite == false){sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_WRITE_ACCESS_DENIED);return;}lock (m_lockObject){try{//先写配置xml文件中这个对象DeviceStorage.ErrorCodes code = m_storage.WriteCommandableProperty(objectId,(BacnetPropertyIds)value.property.propertyIdentifier,value.value[0], value.priority);if (code == DeviceStorage.ErrorCodes.NotForMe)code = m_storage.WriteProperty(objectId,(BacnetPropertyIds)value.property.propertyIdentifier,value.property.propertyArrayIndex,value.value);//回应写的请求if (code == DeviceStorage.ErrorCodes.Good){sender.SimpleAckResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId);}else{if(code == DeviceStorage.ErrorCodes.WriteAccessDenied){sender.ErrorResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_WRITE_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_WRITE_ACCESS_DENIED);}elsesender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}catch{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}/// <summary>///响应读的请求/// </summary>private static void OnReadPropertyRequest(BacnetClient sender, BacnetAddress adr, byte invokeId, BacnetObjectId objectId, BacnetPropertyReference property, BacnetMaxSegments maxSegments){lock (m_lockObject){try{//先看看配置xml文件中是否有这个对象IList<BacnetValue> value;DeviceStorage.ErrorCodes code = m_storage.ReadProperty(objectId, property.GetPropertyId(),property.propertyArrayIndex,out value);if (code == DeviceStorage.ErrorCodes.Good){//回应读的请求sender.ReadPropertyResponse(adr, invokeId,sender.GetSegmentBuffer(maxSegments),objectId, property, value);}else{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}catch{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROPERTY,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}private static void OnReadPropertyMultipleRequest(BacnetClient sender, BacnetAddress adr, byte invokeId, IList<BacnetReadAccessSpecification> properties, BacnetMaxSegments maxSegments){lock(m_lockObject){try{IList<BacnetPropertyValue> value;List<BacnetReadAccessResult> values = new List<BacnetReadAccessResult>();foreach(BacnetReadAccessSpecification p in properties){if(p.propertyReferences.Count == 1 && p.propertyReferences[0].propertyIdentifier == (uint)BacnetPropertyIds.PROP_ALL){if(!m_storage.ReadPropertyAll(p.objectIdentifier, out value)){sender.ErrorResponse(adr,BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROP_MULTIPLE,invokeId, BacnetErrorClasses.ERROR_CLASS_OBJECT,BacnetErrorCodes.ERROR_CODE_UNKNOWN_OBJECT);return;}}else{m_storage.ReadPropertyMultiple(p.objectIdentifier, p.propertyReferences, out value);}values.Add(new BacnetReadAccessResult(p.objectIdentifier, value));}HandleSegmentationResponse(sender, adr, invokeId, maxSegments, (seg) =>{sender.ReadPropertyMultipleResponse(adr, invokeId, seg, values);});}catch{sender.ErrorResponse(adr, BacnetConfirmedServices.SERVICE_CONFIRMED_READ_PROP_MULTIPLE,invokeId, BacnetErrorClasses.ERROR_CLASS_DEVICE,BacnetErrorCodes.ERROR_CODE_OTHER);}}}private static void HandleSegmentationResponse(BacnetClient sender, BacnetAddress adr,byte invoke_id, BacnetMaxSegments max_segments,Action<BacnetClient.Segmentation>transmit){BacnetClient.Segmentation segmentation = sender.GetSegmentBuffer(max_segments);//先发送transmit(segmentation);if (segmentation == null ||segmentation.buffer.result == System.IO.BACnet.Serialize.EncodeResult.Good)return;//启动一个线程去处理段序号 //start  new thread to handle the segment sequenceSystem.Threading.ThreadPool.QueueUserWorkItem((o) =>{byte old_max_info_frames = sender.Transport.MaxInfoFrames;//increase max_info_frames, to increase throughput. This might be against 'standard'//增加max_info_frames,以增加吞吐量。这可能违反“标准”。sender.Transport.MaxInfoFrames = segmentation.window_size;while (true){bool more_follows = (segmentation.buffer.result  &System.IO.BACnet.Serialize.EncodeResult.NotEnoughBuffer) > 0;//等待 segmentACKif((segmentation.sequence_number -1) % segmentation.window_size==0 ||!more_follows){if (!sender.WaitForAllTransmits(sender.TransmitTimeout)){//Transmit timeour;break;}byte current_number = segmentation.sequence_number;if(!sender.WaitForSegmentAck(adr, invoke_id, segmentation, sender.Timeout)){//Didn't get segmentACKbreak;}if (segmentation.sequence_number != current_number){// a retransmitmore_follows = true;}}else{// a negative segmentACK perhapsbyte current_number = segmentation.sequence_number;//didn't waitsender.WaitForSegmentAck(adr, invoke_id, segmentation, 0);if(segmentation.sequence_number != current_number){// a retransmitmore_follows = true;}}if (more_follows)lock (m_lockObject) transmit(segmentation);elsebreak;}sender.Transport.MaxInfoFrames = old_max_info_frames;});}//当其它设备发出WhoIs时,client回答Iamprivate static void OnWhoIs(BacnetClient sender, BacnetAddress adr, int lowLimit, int highLimit){lock (m_lockObject){if (lowLimit != -1 && m_storage.DeviceId < lowLimit) return;else if (highLimit != -1 && m_storage.DeviceId > highLimit) return;else sender.Iam(m_storage.DeviceId, m_supported_segentation);}}}
}

2 MainForm.cs文件

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;using System.IO.BACnet;namespace ElevatorBacnet
{public partial class MainForm : Form{int currentFloor = 1;uint maxFloor = 9;uint minFloor = 1;uint targetFloor = 1;int barkFloor = -1;Button[] btnLayers;bool IsUp = false;bool IsDown = false;bool Remoteconsigne;#region BACnet的属性//模拟输入对象类型BacnetObjectId Bac_TempMinF  = new BacnetObjectId(BacnetObjectTypes.OBJECT_ANALOG_INPUT, 0);BacnetObjectId Bac_TempMaxF = new BacnetObjectId(BacnetObjectTypes.OBJECT_ANALOG_INPUT, 1);//模拟值对象类型BacnetObjectId Bac_TempCurF = new BacnetObjectId(BacnetObjectTypes.OBJECT_ANALOG_VALUE, 0);#endregionpublic MainForm(){ InitializeComponent();Init();timerEle.Enabled = true;timerEle.Start();}/// <summary>/// 初始化UI/// </summary>private void Init(){btnLayers = new Button[]{this.btn1,this.btn2,this.btn3, this.btn4,this.btn5, this.btn6, this.btn7, this.btn8, this.btn9};this.FormClosing += MainForm_FormClosing;}private void MainForm_FormClosing(object sender, FormClosingEventArgs e){this.timerEle.Stop();}private void timerEle_Tick(object sender, EventArgs e){IList<BacnetValue> val = null;BacnetActivity.m_storage.ReadProperty(Bac_TempCurF,BacnetPropertyIds.PROP_OUT_OF_SERVICE, 1,out val);Remoteconsigne = (bool)val[0].Value;if(Remoteconsigne == false){BacnetObjectId d;BacnetValue bv;d = new BacnetObjectId(BacnetObjectTypes.OBJECT_ANALOG_VALUE,(uint)0);bv = BacnetActivity.GetBacnetPresentValue(d);BacnetActivity.SetBacnetPresentValue(Bac_TempCurF, bv);}AnimateData();}private void AnimateData(){BacnetValue bv1, bv2;bv1 = BacnetActivity.GetBacnetPresentValue(Bac_TempMaxF);maxFloor = (uint)bv1.Value;tbMaxFloor.Text = maxFloor.ToString();bv2 = BacnetActivity.GetBacnetPresentValue(Bac_TempMinF);minFloor = (uint)bv2.Value;tbMinFloor.Text = minFloor.ToString();if (currentFloor != targetFloor){barkFloor = currentFloor;if (IsUp){currentFloor++;}else if (IsDown){currentFloor--;}this.btnLayers[barkFloor - 1].BackColor = SystemColors.Highlight;this.btnLayers[currentFloor - 1].BackColor = Color.Red;tbcurrentFloor.Text = currentFloor.ToString();if (Remoteconsigne == false){BacnetActivity.SetBacnetPresentValue(Bac_TempCurF, new BacnetValue(BacnetApplicationTags.BACNET_APPLICATION_TAG_UNSIGNED_INT,(uint)currentFloor));}}else{Reset();}}void Reset (){IsDown = false;IsUp = false;}/// <summary>///上行/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnUp_Click(object sender, EventArgs e){IsUp = true;}/// <summary>/// 下行/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnDown_Click(object sender, EventArgs e){IsDown = true;}private void numTargetFloor_ValueChanged(object sender, EventArgs e){targetFloor = (uint)numTargetFloor.Value;}}
}

3 MainForm 设计器代码:


namespace ElevatorBacnet
{partial class MainForm{/// <summary>/// 必需的设计器变量。/// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// 清理所有正在使用的资源。/// </summary>/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码/// <summary>/// 设计器支持所需的方法 - 不要修改/// 使用代码编辑器修改此方法的内容。/// </summary>private void InitializeComponent(){this.components = new System.ComponentModel.Container();this.gbEleFloor = new System.Windows.Forms.GroupBox();this.btn9 = new System.Windows.Forms.Button();this.btn8 = new System.Windows.Forms.Button();this.btn7 = new System.Windows.Forms.Button();this.btn6 = new System.Windows.Forms.Button();this.btn5 = new System.Windows.Forms.Button();this.btn4 = new System.Windows.Forms.Button();this.btn3 = new System.Windows.Forms.Button();this.btn2 = new System.Windows.Forms.Button();this.btn1 = new System.Windows.Forms.Button();this.btnUp = new System.Windows.Forms.Button();this.numTargetFloor = new System.Windows.Forms.NumericUpDown();this.btnDown = new System.Windows.Forms.Button();this.timerEle = new System.Windows.Forms.Timer(this.components);this.lbMinFloor = new System.Windows.Forms.Label();this.tbMinFloor = new System.Windows.Forms.TextBox();this.tbMaxFloor = new System.Windows.Forms.TextBox();this.lbMaxFloor = new System.Windows.Forms.Label();this.tbcurrentFloor = new System.Windows.Forms.TextBox();this.lbcurrentFloor = new System.Windows.Forms.Label();this.lbtargetFloor = new System.Windows.Forms.Label();this.gbEleFloor.SuspendLayout();((System.ComponentModel.ISupportInitialize)(this.numTargetFloor)).BeginInit();this.SuspendLayout();// // gbEleFloor// this.gbEleFloor.Controls.Add(this.btn9);this.gbEleFloor.Controls.Add(this.btn8);this.gbEleFloor.Controls.Add(this.btn7);this.gbEleFloor.Controls.Add(this.btn6);this.gbEleFloor.Controls.Add(this.btn5);this.gbEleFloor.Controls.Add(this.btn4);this.gbEleFloor.Controls.Add(this.btn3);this.gbEleFloor.Controls.Add(this.btn2);this.gbEleFloor.Controls.Add(this.btn1);this.gbEleFloor.Location = new System.Drawing.Point(31, 7);this.gbEleFloor.Name = "gbEleFloor";this.gbEleFloor.Size = new System.Drawing.Size(200, 435);this.gbEleFloor.TabIndex = 0;this.gbEleFloor.TabStop = false;this.gbEleFloor.Text = "电梯楼层";// // btn9// this.btn9.BackColor = System.Drawing.SystemColors.Highlight;this.btn9.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn9.Location = new System.Drawing.Point(6, 15);this.btn9.Name = "btn9";this.btn9.Size = new System.Drawing.Size(73, 41);this.btn9.TabIndex = 8;this.btn9.Text = "9楼";this.btn9.UseVisualStyleBackColor = false;// // btn8// this.btn8.BackColor = System.Drawing.SystemColors.Highlight;this.btn8.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn8.Location = new System.Drawing.Point(6, 61);this.btn8.Name = "btn8";this.btn8.Size = new System.Drawing.Size(73, 41);this.btn8.TabIndex = 7;this.btn8.Text = "8楼";this.btn8.UseVisualStyleBackColor = false;// // btn7// this.btn7.BackColor = System.Drawing.SystemColors.Highlight;this.btn7.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn7.Location = new System.Drawing.Point(6, 107);this.btn7.Name = "btn7";this.btn7.Size = new System.Drawing.Size(73, 41);this.btn7.TabIndex = 6;this.btn7.Text = "7楼";this.btn7.UseVisualStyleBackColor = false;// // btn6// this.btn6.BackColor = System.Drawing.SystemColors.Highlight;this.btn6.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn6.Location = new System.Drawing.Point(6, 153);this.btn6.Name = "btn6";this.btn6.Size = new System.Drawing.Size(73, 41);this.btn6.TabIndex = 5;this.btn6.Text = "6楼";this.btn6.UseVisualStyleBackColor = false;// // btn5// this.btn5.BackColor = System.Drawing.SystemColors.Highlight;this.btn5.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn5.Location = new System.Drawing.Point(6, 199);this.btn5.Name = "btn5";this.btn5.Size = new System.Drawing.Size(73, 41);this.btn5.TabIndex = 4;this.btn5.Text = "5楼";this.btn5.UseVisualStyleBackColor = false;// // btn4// this.btn4.BackColor = System.Drawing.SystemColors.Highlight;this.btn4.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn4.Location = new System.Drawing.Point(6, 245);this.btn4.Name = "btn4";this.btn4.Size = new System.Drawing.Size(73, 41);this.btn4.TabIndex = 3;this.btn4.Text = "4楼";this.btn4.UseVisualStyleBackColor = false;// // btn3// this.btn3.BackColor = System.Drawing.SystemColors.Highlight;this.btn3.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn3.Location = new System.Drawing.Point(6, 291);this.btn3.Name = "btn3";this.btn3.Size = new System.Drawing.Size(73, 41);this.btn3.TabIndex = 2;this.btn3.Text = "3楼";this.btn3.UseVisualStyleBackColor = false;// // btn2// this.btn2.BackColor = System.Drawing.SystemColors.Highlight;this.btn2.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn2.Location = new System.Drawing.Point(6, 337);this.btn2.Name = "btn2";this.btn2.Size = new System.Drawing.Size(73, 41);this.btn2.TabIndex = 1;this.btn2.Text = "2楼";this.btn2.UseVisualStyleBackColor = false;// // btn1// this.btn1.BackColor = System.Drawing.SystemColors.Highlight;this.btn1.Font = new System.Drawing.Font("微软雅黑", 10.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn1.Location = new System.Drawing.Point(6, 383);this.btn1.Name = "btn1";this.btn1.Size = new System.Drawing.Size(73, 41);this.btn1.TabIndex = 0;this.btn1.Text = "1楼";this.btn1.UseVisualStyleBackColor = false;// // btnUp// this.btnUp.Location = new System.Drawing.Point(271, 183);this.btnUp.Name = "btnUp";this.btnUp.Size = new System.Drawing.Size(78, 35);this.btnUp.TabIndex = 1;this.btnUp.Text = "Up";this.btnUp.UseVisualStyleBackColor = true;this.btnUp.Click += new System.EventHandler(this.btnUp_Click);// // numTargetFloor// this.numTargetFloor.Location = new System.Drawing.Point(377, 133);this.numTargetFloor.Maximum = new decimal(new int[] {9,0,0,0});this.numTargetFloor.Minimum = new decimal(new int[] {1,0,0,0});this.numTargetFloor.Name = "numTargetFloor";this.numTargetFloor.Size = new System.Drawing.Size(73, 25);this.numTargetFloor.TabIndex = 2;this.numTargetFloor.Value = new decimal(new int[] {1,0,0,0});this.numTargetFloor.ValueChanged += new System.EventHandler(this.numTargetFloor_ValueChanged);// // btnDown// this.btnDown.Location = new System.Drawing.Point(387, 183);this.btnDown.Name = "btnDown";this.btnDown.Size = new System.Drawing.Size(78, 35);this.btnDown.TabIndex = 3;this.btnDown.Text = "Down";this.btnDown.UseVisualStyleBackColor = true;this.btnDown.Click += new System.EventHandler(this.btnDown_Click);// // timerEle// this.timerEle.Interval = 1000;this.timerEle.Tick += new System.EventHandler(this.timerEle_Tick);// // lbMinFloor// this.lbMinFloor.AutoSize = true;this.lbMinFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.lbMinFloor.Location = new System.Drawing.Point(267, 29);this.lbMinFloor.Name = "lbMinFloor";this.lbMinFloor.Size = new System.Drawing.Size(104, 19);this.lbMinFloor.TabIndex = 4;this.lbMinFloor.Text = "最低楼层:";// // tbMinFloor// this.tbMinFloor.Enabled = false;this.tbMinFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.tbMinFloor.Location = new System.Drawing.Point(377, 26);this.tbMinFloor.Name = "tbMinFloor";this.tbMinFloor.Size = new System.Drawing.Size(73, 28);this.tbMinFloor.TabIndex = 5;this.tbMinFloor.Text = "1";// // tbMaxFloor// this.tbMaxFloor.Enabled = false;this.tbMaxFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.tbMaxFloor.Location = new System.Drawing.Point(377, 60);this.tbMaxFloor.Name = "tbMaxFloor";this.tbMaxFloor.Size = new System.Drawing.Size(73, 28);this.tbMaxFloor.TabIndex = 7;this.tbMaxFloor.Text = "9";// // lbMaxFloor// this.lbMaxFloor.AutoSize = true;this.lbMaxFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.lbMaxFloor.Location = new System.Drawing.Point(267, 63);this.lbMaxFloor.Name = "lbMaxFloor";this.lbMaxFloor.Size = new System.Drawing.Size(104, 19);this.lbMaxFloor.TabIndex = 6;this.lbMaxFloor.Text = "最高楼层:";// // tbcurrentFloor// this.tbcurrentFloor.Enabled = false;this.tbcurrentFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.tbcurrentFloor.Location = new System.Drawing.Point(377, 94);this.tbcurrentFloor.Name = "tbcurrentFloor";this.tbcurrentFloor.Size = new System.Drawing.Size(73, 28);this.tbcurrentFloor.TabIndex = 9;this.tbcurrentFloor.Text = "1";// // lbcurrentFloor// this.lbcurrentFloor.AutoSize = true;this.lbcurrentFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.lbcurrentFloor.Location = new System.Drawing.Point(267, 97);this.lbcurrentFloor.Name = "lbcurrentFloor";this.lbcurrentFloor.Size = new System.Drawing.Size(104, 19);this.lbcurrentFloor.TabIndex = 8;this.lbcurrentFloor.Text = "当前楼层:";// // lbtargetFloor// this.lbtargetFloor.AutoSize = true;this.lbtargetFloor.Font = new System.Drawing.Font("宋体", 10.8F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.lbtargetFloor.Location = new System.Drawing.Point(267, 133);this.lbtargetFloor.Name = "lbtargetFloor";this.lbtargetFloor.Size = new System.Drawing.Size(104, 19);this.lbtargetFloor.TabIndex = 10;this.lbtargetFloor.Text = "目标楼层:";// // MainForm// this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(550, 454);this.Controls.Add(this.lbtargetFloor);this.Controls.Add(this.tbcurrentFloor);this.Controls.Add(this.lbcurrentFloor);this.Controls.Add(this.tbMaxFloor);this.Controls.Add(this.lbMaxFloor);this.Controls.Add(this.tbMinFloor);this.Controls.Add(this.lbMinFloor);this.Controls.Add(this.btnDown);this.Controls.Add(this.numTargetFloor);this.Controls.Add(this.btnUp);this.Controls.Add(this.gbEleFloor);this.Name = "MainForm";this.Text = "电梯BACnet---By唠嗑一下";this.gbEleFloor.ResumeLayout(false);((System.ComponentModel.ISupportInitialize)(this.numTargetFloor)).EndInit();this.ResumeLayout(false);this.PerformLayout();}#endregionprivate System.Windows.Forms.GroupBox gbEleFloor;private System.Windows.Forms.Button btn1;private System.Windows.Forms.Button btn9;private System.Windows.Forms.Button btn8;private System.Windows.Forms.Button btn7;private System.Windows.Forms.Button btn6;private System.Windows.Forms.Button btn5;private System.Windows.Forms.Button btn4;private System.Windows.Forms.Button btn3;private System.Windows.Forms.Button btn2;private System.Windows.Forms.Button btnUp;private System.Windows.Forms.NumericUpDown numTargetFloor;private System.Windows.Forms.Button btnDown;private System.Windows.Forms.Timer timerEle;private System.Windows.Forms.Label lbMinFloor;private System.Windows.Forms.TextBox tbMinFloor;private System.Windows.Forms.TextBox tbMaxFloor;private System.Windows.Forms.Label lbMaxFloor;private System.Windows.Forms.TextBox tbcurrentFloor;private System.Windows.Forms.Label lbcurrentFloor;private System.Windows.Forms.Label lbtargetFloor;}
}

4 Program.cs文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Threading;using System.Windows.Forms;namespace ElevatorBacnet
{static class Program{public static int Count;public static int DeviceId = -1;public static string IPAddress = "Default";/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(string[] args){if(args != null && (args.Length >= 1)){if (Int32.TryParse(args[0], out DeviceId) == false)DeviceId = -1;}if(args != null && args.Length == 2){IPAddress = args[1];}/* Semaphore s = new Semaphore(63, 63, "ElevatorBanet{FAED-FAED}");if(s.WaitOne() == true){Count = 64 - s.Release();s.WaitOne();}*/try{BacnetActivity.ReInitialize();Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new MainForm());}catch(Exception ex){MessageBox.Show("Fatal Error", "ElevatorBanet",MessageBoxButtons.OK, MessageBoxIcon.Error);}// s.Release();}}
}

5 DeviceStorage.xml

<?xml version="1.0"?>
<DeviceStorage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Objects><!--设备对象--><Object Type="OBJECT_DEVICE" Instance="64237"><Properties><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_DEVICE:64237</Value></Property><Property Id="PROP_OBJECT_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>ElevatorBacnet</Value></Property><Property Id="PROP_OBJECT_TYPE" TAG="BACNET_APPLICATION_TAG_ENUMERATED"><Value>8</Value></Property><Property Id="PROP_SYSTEM_STATUS" TAG="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_VENDOR_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>F. Chaxel,Thanks to Morten Kvistgaard,MIT license,2015</Value></Property><Property Id="PROP_VENDOR_IDENTIFIER" TAG="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>61440</Value></Property><Property Id="PROP_MODEL_NAME" TAG="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Hpmont_FC_2024</Value></Property><Property Id="PROP_FIRMWARE_REVISION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>0.1.0</Value></Property><Property Id="PROP_APPLICATION_SOFTWARE_VERSION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>0.1.0</Value></Property><Property Id="PROP_PROTOCOL_VERSION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1</Value></Property><Property Id="PROP_PROTOCOL_REVISION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>14</Value></Property><Property Id="PROP_PROTOCOL_SERVICES_SUPPORTED" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>01111111101111000011101110000000011010101</Value></Property><Property Id="PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000000010101010000000000000000100000000111110111111111</Value></Property><Property Id="PROP_OBJECT_LIST" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"></Property><Property Id="PROP_MAX_APDU_LENGTH_ACCEPTED" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1476</Value></Property><Property Id="PROP_SEGMENTATION_SUPPORTED" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>3</Value></Property><Property Id="PROP_APDU_TIMEOUT" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3000</Value></Property><Property Id="PROP_NUMBER_OF_APDU_RETRIES" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3</Value></Property><Property Id="PROP_DEVICE_ADDRESS_BINDING" Tag="BACNET_APPLICATION_TAG_NULL"/><Property Id="PROP_DATABASE_REVISION" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>0</Value></Property><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Free ElevatorController Simulator, X,xm, 2024</Value></Property><Property Id="PROP_LOCATION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>China.Shenzhen</Value></Property></Properties></Object><!--模拟输入 0--><Object Type="OBJECT_ANALOG_INPUT" Instance="0"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Elevator Min Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_INPUT:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Min.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>1</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value><!--UNITS_NO_UNITS--></Property></Properties></Object><!--模拟输入 1--><Object Type="OBJECT_ANALOG_INPUT" Instance="1"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Elevator Max Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_INPUT:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Max.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>9</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value><!--UNITS_NO_UNITS--></Property></Properties></Object><!--模拟值--><Object Type="OBJECT_ANALOG_VALUE" Instance="0"><Properties><Property Id="PROP_DESCRIPTION" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Current Layer</Value></Property><Property Id="PROP_EVENT_STATE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_OBJECT_IDENTIFIER" Tag="BACNET_APPLICATION_TAG_OBJECT_ID"><Value>OBJECT_ANALOG_VALUE:0</Value></Property><Property Id="PROP_OBJECT_NAME" Tag="BACNET_APPLICATION_TAG_CHARACTER_STRING"><Value>Current.Layer</Value></Property><Property Id="PROP_OBJECT_TYPE" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>2</Value></Property><Property Id="PROP_OUT_OF_SERVICE" Tag="BACNET_APPLICATION_TAG_BOOLEAN"><Value>False</Value></Property><Property Id="PROP_PRESENT_VALUE" Tag="BACNET_APPLICATION_TAG_UNSIGNED_INT"><Value>3</Value></Property><Property Id="PROP_RELIABILITY" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>0</Value></Property><Property Id="PROP_STATUS_FLAGS" Tag="BACNET_APPLICATION_TAG_BIT_STRING"><Value>0000</Value></Property><Property Id="PROP_UNITS" Tag="BACNET_APPLICATION_TAG_ENUMERATED"><Value>95</Value></Property></Properties></Object></Objects>
</DeviceStorage>

小结

简要介绍了BACnet模拟设备,包含基本的读取功能, 订阅功能,返回对象列表属性功能。 完整工程代码下载地址:https://download.csdn.net/download/weixin_40314351/89161436

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/641716.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA 使用Debug时IOC容器内容有序排列

勾选后如下图所示便会按照首字母进行排列

C++ 面向对象-封装

C 是一种多范式编程语言&#xff0c;它支持面向对象编程&#xff08;OOP&#xff09;范式。面向对象编程是一种程序设计思想&#xff0c;其中程序由对象组成&#xff0c;每个对象都是一个实例&#xff0c;具有数据和相关操作。在C中&#xff0c;实现面向对象编程主要通过类和对…

C#-使用Harmony库实现DLL文件反射调用

一. Harmony工作原理 利用C#运行时Runtime的反射机制,动态加载dll中的方法,字段,属性,实现对DLL方法的重写和代码注入。 二. Harmony下载及安装 1.下载Harmony_lib库lib.harmony.2.3.3.nupkg 霸王•吕布 / CSharpHarmonyLib GitCodehttps://gitcode.net/qq_35829452/csharph…

yolov8 裁剪检测结果

yolov8 裁剪检测结果 1. 基础2. 图片批量裁剪2.1 检测裁剪2.2 分割裁剪 3. 视频裁剪3.1 检测裁剪3.2 分割裁剪3.3 实时裁剪 4. 源码 1. 基础 本项目是在 WindowsYOLOV8环境配置 的基础上实现的 思路&#xff1a;将检测得到的物体边框提取&#xff0c;然后边框裁剪原图&#xf…

锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测 Matlab基于GRU的锂电池剩余寿命预测 基于GRU的锂电池剩余寿命预测&#xff08;单变量&#xff09; 运行环境Matlab2020及以上 锂电池的剩余寿命预测是…

设计模式学习笔记 - 开源实战四(中):剖析Spring框架中用来支持扩展的设计模式

概述 上篇文章&#xff0c;学习了 Spring 框架背后蕴含的设计思想&#xff0c;比如约定优于配置、低侵入松耦合、模块化轻量级等等。这些设计思想可以借鉴到其他框架开发中&#xff0c;在大的设计层面提高框架的代码质量。 除了上篇文章降到的设计思想&#xff0c;实际上&…

C++——类和对象练习(日期类)

日期类 1. 构造函数和析构函数2. 拷贝构造和赋值运算符重载3. 运算符重载3.1 日期的比较3.2 日期加减天数3.3 日期减日期3.4 流插入和流提取 4. 取地址和const取地址重载5. 完整代码Date.hDate.c 对日期类进行一个完善&#xff0c;可以帮助我们理解六个默认成员函数&#xff0c…

【黑马头条】-day12项目部署和发布-jenkins

文章目录 1 持续集成2 软件开发模式2.1 瀑布模式2.2 敏捷开发2.2.1 迭代开发2.2.2 增量开发 3 Jenkins3.1 Jenkins安装3.1.1 导入镜像3.1.2 配置3.1.3 初始化设置 3.2 插件安装3.3 服务器环境准备3.3.1 Docker安装配置3.3.2 Git安装配置3.3.3 Maven安装配置 3.4 Jenkins工具配置…

盲人过马路安全:科技力量赋予“隐形守护者”

作为一名资深记者&#xff0c;我始终关注着社会各群体的生活现状&#xff0c;尤其是那些面临特殊挑战的人群。今天&#xff0c;我想聚焦一个看似平常却对盲人构成重大困扰的日常场景——过马路&#xff0c;以及一款名为蝙蝠避障的辅助应用如何成为他们的盲人过马路安全的守护者…

Docker 部署 WordPress 并完成建站

什么是 WordPress WordPress 是使用 PHP 语言开发的博客平台&#xff0c;用户可以在支持 PHP 和 MySQL 数据库的服务器上架设属于自己的网站。也可以把 WordPress 当作一个内容管理系统&#xff08;CMS&#xff09;来使用。WordPress 是一款个人博客系统&#xff0c;并逐步演化…

【Linux系统编程】第七弹---权限管理操作(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、修改文件权限的做法(一) 2、有无权限的表现 总结 上一弹我们讲解了Linux权限概念相关的知识&#xff0c;但是我们只知道有…

【C++学习】STL之空间配置器之一级空间配置器

文章目录 &#x1f4ca;什么是空间配置器✈STL 提供六大组件的了解&#x1f440;为什么需要空间配置器&#x1f44d;SGI-STL空间配置器实现原理&#x1f302;一级空间配置器的实现 &#x1f4ca;什么是空间配置器 空间配置器&#xff0c;顾名思义就是为各个容器高效的管理空间…