模拟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
            {
                //init
                if (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);
                }
                else
                    deviceId = (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 point
                BacnetIpUdpProtocolTransport udp = new
                    BacnetIpUdpProtocolTransport(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 index
                    value = 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 whole
                    BacnetValue[] 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 false
                b.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;
               //可以是任何model
                v.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 vers
                    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.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);
                        }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);
                }
            }
        }

        /// <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
            {
                //init
                if (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);
                }
                else
                    deviceId = (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 point
                BacnetIpUdpProtocolTransport udp = new
                    BacnetIpUdpProtocolTransport(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);
            }
        }
        #endregion

        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 index
                    value = 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 whole
                    BacnetValue[] 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 false
                b.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;
               //可以是任何model
                v.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 vers
                    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.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");

                    //crceate
                    Subscription sub = HandleSubscriptionRequest(sender, adr,
                        invokeId, subscriberProcessIdentifier,
                        monitoredObjectIdentifier, (uint)BacnetPropertyIds.PROP_ALL,
                        cancellationRequest, issueConfirmedNotifications, lifetime,
                        covIncrement);

                    //send confirm
                    sender.SimpleAckResponse(adr,
                        BacnetConfirmedServices.SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY,
                        invokeId);

                    //also send first values
                    if (!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);
                        }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);
                }
            }
        }

        /// <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 sequence
            System.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;

                    //等待 segmentACK
                    if((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 segmentACK
                            break;
                        }
                        if (segmentation.sequence_number != current_number)
                        {
                            // a retransmit
                            more_follows = true;
                        }
                    }
                    else
                    {
                        // a negative segmentACK perhaps
                        byte current_number = segmentation.sequence_number;
                        //didn't wait
                        sender.WaitForSegmentAck(adr, invoke_id, segmentation, 0);
                        if(segmentation.sequence_number != current_number)
                        {
                            // a retransmit
                            more_follows = true;
                        }
                    }

                    if (more_follows)
                        lock (m_lockObject) transmit(segmentation);
                    else
                        break;
                }
                sender.Transport.MaxInfoFrames = old_max_info_frames;
            });
        }

        //当其它设备发出WhoIs时,client回答Iam
        private 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);

        #endregion

        public 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();

        }

        #endregion

        private 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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Collection与数据结构] PriorityQueue与堆

1. 优先级队列 1.1 概念 前面介绍过队列&#xff0c;队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队列时&#xff0c;可能需要优先级高的元素先出队列&#xff0c;该中场景下&#xff0c;使用队列显然…

Rust - 引用和借用

上一篇章末尾提到&#xff0c;如果仅仅支持通过转移所有权的方式获取一个值&#xff0c;那会让程序变得复杂。 Rust 能否像其它编程语言一样&#xff0c;使用某个变量的指针或者引用呢&#xff1f;答案是可以。 Rust 通过 借用(Borrowing) 这个行为来达成上述的目的&#xff0…

深入探索GDB:Linux下强大的调试神器

目录 一、GDB简介&#xff1a;源码级调试的基石 二、GDB基础操作&#xff1a;从入门到熟练 启动与基本命令 三、GDB进阶功能&#xff1a;解锁更深层次的调试能力 1. 回溯追踪&#xff1a;洞察调用栈 2. 动态内存检测&#xff1a;揪出内存问题 3. 条件断点与观察点&#…

JavaSE——程序逻辑控制

1. 顺序结构 顺序结构 比较简单&#xff0c;按照代码书写的顺序一行一行执行。 例如&#xff1a; public static void main(String[] args) {System.out.println(111);System.out.println(222);System.out.println(333);} 运行结果如下&#xff1a; 如果调整代码的书写顺序 , …

C++:继承作业题

1. 关于以下菱形继承说法不正确的是&#xff08; &#xff09; &#xfeff;class B {public: int b;};class C1: public B {public: int c1;};class C2: public B {public: int c2;};class D : public C1, public C2 {public: int d;};A.D总共占了20个字节B.B中的内容总共在D…

PE文件格式

PE文件格式 PE头&#xff1a;DOS头DOS存根NT头NT头&#xff1a;文件头NT头&#xff1a;可选头 节区头.text(代码)(节区头).data(数据)(节区头).rdata.idata&#xff0c;导入表 最后给出一个PE文件的16进制编辑器中的截图&#xff0c;找到其中每一个头的信息&#xff0c;和导入表…

2015NOIP普及组真题 3. 求和

线上OJ&#xff1a; 一本通&#xff1a;http://ybt.ssoier.cn:8088/problem_show.php?pid1971 核心思想&#xff1a; 本题的约束条件有两个&#xff1a; 条件1、colorx colorz 条件2、x、y、z的坐标满足 y − x z − y&#xff08;即 y 在 x 和 z 的中心位置&#xff09; …

scipy csr_matrix: understand indptr

See https://stackoverflow.com/questions/52299420/scipy-csr-matrix-understand-indptr

Esp8266 - USB开关分享(开源)

文章目录 简介推广自己gitee项目地址:嘉立创项目地址&#xff1a;联系我们 功能演示视频原理图嘉立创PCB开源地址原理图PCB预览 固件烧录代码编译烧录1. 软件和驱动安装2. 代码编译1. 安装所需要的依赖库文件2. 下载源代码3. 烧录代码 使用说明1. 设备配网2. 打开设备操作页面3…

NAT的知识点和实现

1.NAT的作用&#xff1a; &#xff08;1&#xff09;、把内网私网IP转换公网IP&#xff1b; &#xff08;2&#xff09;、隐藏内网&#xff0c;起到保护内网作用&#xff1b; &#xff08;3&#xff09;、适当的缓解的IPv4地址空间枯竭&#xff1b; &#xff08;4&#xff…

[RTOS 学习记录] 复杂工程项目的管理

[RTOS 学习记录] 复杂工程项目的管理 这篇文章是我阅读《嵌入式实时操作系统μCOS-II原理及应用》后的读书笔记&#xff0c;记录目的是为了个人后续回顾复习使用。 前置内容&#xff1a; 工程管理工具make及makefile 文章目录 1 批处理文件与makefile的综合使用1.1 批处理文件…

Qt实现XYModem协议(五)

1 概述 XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据&#xff0c;并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K&am…

4月23号总结

java实现发送邮件 在做聊天室项目的时候&#xff0c;由于需要发送邮箱验证码&#xff0c;所以自己查找了这方面的内容。 首先需要在Maven里面依赖 <dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><versio…

英伟达AI系列免费公开课

英伟达公开课官网地址 Augment your LLM Using Retrieval Augmented Generation Building RAG Agents with LLMs langchain的workflow: ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c90cb157c9c84bb5b3da380ec56f5c2a.png Generative AI Explained

Linux系统中安装MySQL

1、在电脑中安装虚拟机 2、df -h查看光盘是否挂载&#xff0c;没挂载用mount -o ro /dev/sr0 /media命令挂载 3、进入etc/yum.repos.d目录查看仓是否配置&#xff0c;若配置进行下一一步&#xff0c;未配置则进行配置 配置软件仓库 [rootlocalhost yum.repos.d]# vim rhle.r…

Linux中文件描述符与重定向的深入探索

目录 1. 理解C语言的文件操作函数 2. 操作系统的文件操作接口 3. 文件描述符详解和其内核本质 4. 如何理解Linux下一切皆文件 5. Linux中的重定向 5.1 输出重定向 5.2 追加重定向 5.3 输入重定向 6. 结合文件描述符理解重定向 7.重定向的系统调用 在Linux操作系统中&a…

springboot整合mybatis-plus模版

1.创建springboot项目 Maven类型Lombok依赖Spring Web 依赖MySQL Driver依赖pom.xml&#xff1a;<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/…

上门废品回收小程序,互联网回收拥有哪些特点?

随着社会的进步&#xff0c;人们的生活水平不断提高&#xff0c;产生的可回收物也在不断上升&#xff0c;每年垃圾站都能产生大量的可回收物&#xff0c;这也造成了资源的浪费。 目前&#xff0c;加快发展回收模式&#xff0c;提高我国回收效率成为了当下回收市场发展的重要方…

[笔试强训day04]

文章目录 WY22 Fibonacci数列NC242 单词搜索BC140 杨辉三角 WY22 Fibonacci数列 WY22 Fibonacci数列 #include<iostream> #include<cmath>using namespace std;int n;int main() {cin>>n;int a0,b1,c1;while(n>c){ab;bc;cab;}int ansmin(n-b,c-n);cout&l…

windows mysql8 安装后 提示密码不对,修改下密码认证方式就可以了

Windows上安装MySQL8后提示密码不对的问题可以通过以下步骤解决&#xff1a; 安装MySQL8 首先&#xff0c;你需要下载并安装MySQL8。你可以从MySQL官方网站下载符合你操作系统版本的安装包。 安装地址是&#xff1a;MySQL :: Download MySQL Installer 安装过程中&#xff…