今天来捋一捋BACnet的读写服务,自己动手写一个小Demo,能够读取Bacnet.Room.Simulator的数据。
先看一下效果图
大体思路
先发现BACnet设备,发现的设备记录在左上角的树中, 点击设备后能够获取该设备的所有对象,用左下角的树形显示。 选中某个对象后,再点读取,右边的方框输出读写日志。
发现BACnet设备
首先我们创建一个BACnet对象,用其来发现设备。
RWServices.cs 辅助类,定义了一个BACnet对象,以及声明了存储发现的BACnet设备的字典。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.BACnet;
namespace RW_BacnetClient
{
public static class RWServices
{
public static BacnetClient m_service;
public static Dictionary<BacnetAddress, BacnetLine> clients
= new Dictionary<BacnetAddress, BacnetLine>();
}
public class BacnetLine
{
public BacnetClient client;
public uint deviceId;
public BacnetLine()
{
}
public BacnetLine(BacnetClient client, uint deviceId)
{
this.client = client;
this.deviceId = deviceId;
}
}
}
主界面代码初始化,初始化设备树,以及发现设备。
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;
using System.IO.BACnet.Base;
using System.Threading;
namespace RW_BacnetClient
{
public partial class Form1 : Form
{
TreeNode deviceRoot;
public Form1()
{
InitializeComponent();
InitUI();
Init();
RegisterEvent();
}
//界面初始化
private void InitUI()
{
treeDevice.ImageList = imageList1;
deviceRoot = new TreeNode("根", 0, 0);
treeDevice.Nodes.Add(deviceRoot);
treeProperty.ImageList = imageList1;
}
private void Init()
{
RWServices.m_service = new BacnetClient(
new BacnetIpUdpProtocolTransport(47808, false));
RWServices.m_service.OnIam -= M_service_OnIam;
RWServices.m_service.OnIam += M_service_OnIam;
//启动
RWServices.m_service.Start();
//广播 谁在
RWServices.m_service.WhoIs();
}
//事件注册
private void RegisterEvent()
{
btnWhoIs.Click += BtnWhoIs_Click;
btnRead.Click += BtnRead_Click;
btnReadMutil.Click += BtnReadMutil_Click;
btnSubscribe.Click += BtnSubscribe_Click;
btnWrite.Click += BtnWrite_Click;
treeDevice.NodeMouseClick += TreeDevice_NodeMouseClick;
treeProperty.NodeMouseClick += TreeProperty_NodeMouseClick;
}
#region Helper Method
private void Log(string str)
{
this.BeginInvoke(new Action(() =>
{
this.txt_MsgList.AppendText($"{System.DateTime.Now.ToString("HH:mm:ss")}: {str}\r\n");
}));
}
private void ShowText(string str)
{
this.BeginInvoke(new Action(() =>
{
this.txt_MsgList.AppendText(str + "\r\n");
}));
}
#endregion
/// <summary>
/// 响应Iam
/// </summary>
private void M_service_OnIam(BacnetClient sender, BacnetAddress adr, uint deviceId, uint maxAPDU, BacnetSegmentations segmentation, ushort vendorId)
{
if (RWServices.clients.ContainsKey(adr)) return;
RWServices.clients[adr] = new BacnetLine(sender, deviceId); //记录新添加的BACnet设备
sender.OnCOVNotification += Sender_OnCOVNotification; ;
this.BeginInvoke(new Action(() =>
{//添加到设备树上
TreeNode node = new TreeNode(adr.ToString() + ":" + deviceId, 1, 1);
node.Tag = adr;
deviceRoot.Nodes.Add(node);
}));
}
读取设备的所有对象
像Yabe一样,获取BACnet设备的所有对象,应该如何获取呢?
当然也是使用ReadPropertyRequest
服务,读取设备对象BacnetObjectTypes.OBJECT_DEVICE
,读整个对象列表 BacnetPropertyIds.PROP_OBJECT_LIST
/// <summary>
/// 设备树
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TreeDevice_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (!(e.Node is TreeNode node)) return;
if (node.Level == 0 || node.Tag == null) return;
BacnetLine line = RWServices.clients[node.Tag as BacnetAddress];
ReadDeviceObject(line, (BacnetAddress)(node.Tag));
}
#region 读取设备的所有对象
private void ReadDeviceObject( BacnetLine comm, BacnetAddress adr)
{
uint device_id = comm.deviceId;
IList<BacnetValue> ret; // 得到所有的对象
if (!RWServices.m_service.ReadPropertyRequest(adr,
new BacnetObjectId(BacnetObjectTypes.OBJECT_DEVICE, device_id),
BacnetPropertyIds.PROP_OBJECT_LIST, out ret))
{
Log("Didn't get response from 'Structured Object List'");
return;
}
List<BacnetObjectId> listObj = SortBacnetObjects(ret);
this.BeginInvoke(new Action(() =>
{
this.treeProperty.Nodes.Clear();
foreach(BacnetObjectId ob in listObj)
{
TreeNode node = new TreeNode(ob.ToString(), 2, 2);
node.Tag = ob;
treeProperty.Nodes.Add(node);
}
}));
}
//排序
private List<BacnetObjectId> SortBacnetObjects(IList<BacnetValue> listVal)
{
List<BacnetObjectId> sortedList = new List<BacnetObjectId>();
foreach(BacnetValue val in listVal)
{
if(val.Value is BacnetObjectId)
{
sortedList.Add((BacnetObjectId)val.Value);
}
else
{
BacnetDeviceObjectPropertyReference v = (BacnetDeviceObjectPropertyReference)val.Value;
sortedList.Add(v.objectIdentifier);
}
}
sortedList.Sort();
return sortedList;
}
#endregion
读取设备指定的对象的当前值
如果我们仅读取单一对象的当前值,应该如何读取呢?就比如说读取当前的温度值。
首先当然是找到当前温度值这个对象,然后再去读取当前值(是众多属性中的其中一个)了。
ReadPropertyRequest(adr, objectId, BacnetPropertyIds.PROP_PRESENT_VALUE, out values)
;
//读取单个
private void BtnRead_Click(object sender, EventArgs e)
{
if (treeProperty.SelectedNode == null
|| treeDevice.SelectedNode == null)
return;
BacnetAddress adr = (BacnetAddress)treeDevice.SelectedNode.Tag;
if (adr == null) return;
//找到待读取的对象
BacnetObjectId objectId = (BacnetObjectId)treeProperty.SelectedNode.Tag ;
if (objectId == null) return;
IList<BacnetValue> values;
//读取该对象的当前值
if( RWServices.m_service.ReadPropertyRequest(adr, objectId,
BacnetPropertyIds.PROP_PRESENT_VALUE, out values))
{
DataType = values[0].Value.GetType();
ShowText(objectId.ToString() + ": 读到值:" + values[0].Value.ToString() +
" 数据类型:" + DataType +
" 转换后的类型:" + Type.GetTypeCode(DataType));
}
}
读某个对象下的所有属性Property
读取所有属性ReadPropertyMultipleRequest(adr, objectId, properties, out multi_value_list)
//读多个属性
private void BtnReadMutil_Click(object sender, EventArgs e)
{
; if (treeProperty.SelectedNode == null
|| treeDevice.SelectedNode == null)
return;
BacnetAddress adr = (BacnetAddress)treeDevice.SelectedNode.Tag;
if (adr == null) return;
//找到某个对象id
BacnetObjectId objectId = (BacnetObjectId)treeProperty.SelectedNode.Tag;
if (objectId == null) return;
System.Threading.ThreadPool.QueueUserWorkItem((o =>
{
BacnetPropertyReference[] properties = new BacnetPropertyReference[]
{
new BacnetPropertyReference((uint)BacnetPropertyIds.PROP_ALL,
System.IO.BACnet.Serialize.ASN1.BACNET_ARRAY_ALL)
};
IList<BacnetReadAccessResult> multi_value_list;
if (!RWServices.m_service.ReadPropertyMultipleRequest(adr, objectId,
properties, out multi_value_list))
{
Log("读取多个属性失败");
return;
}
foreach (BacnetPropertyValue p_value in multi_value_list[0].values)
{
object value = null;
BacnetValue[] b_values = null;
if (p_value.value != null)
{
b_values = new BacnetValue[p_value.value.Count];
p_value.value.CopyTo(b_values, 0);
if (b_values.Length > 1)
{
object[] arr = new object[b_values.Length];
for (int j = 0; j < arr.Length; j++)
arr[j] = b_values[j].Value;
value = arr;
}
else if (b_values.Length == 1)
value = b_values[0].Value;
}
else
b_values = new BacnetValue[0];
ShowText("propertyId: " + p_value.property.propertyIdentifier.ToString()+
" properStr: " + p_value.property.ToString() +
" 读到值:" + value.ToString() +
" 数据类型:" + value.GetType().ToString());
/* switch ((BacnetPropertyIds)p_value.property.propertyIdentifier)
{
// PROP_PRESENT_VALUE can be write at null value to clear the prioroityarray if exists
case BacnetPropertyIds.PROP_PRESENT_VALUE:
// change to the related nullable type
Type t = value.GetType();
try
{
if (t != typeof(String)) // a bug on linuxmono where the folling instruction generates a wrong type
t = Type.GetType("System.Nullable`1[" + value.GetType().FullName + "]");
}
catch { }
break;
default:
break;
}
// The Prop Name replace the PropId into the Treenode
if (p_value.property.propertyIdentifier == (byte)BacnetPropertyIds.PROP_OBJECT_NAME)
{
ShowText(objectId.ToString() + ": 读到值:" + value.ToString());
}*/
}
}));
}
改写某个属性的值
要改写某个属性的值,首先要将out_of _service
(脱离服务)的值设置为true, 可以先用Yabe设置。
再用自己写的Demo修改值,当然并不是所有值都可以修改。
WritePropertyRequest(adr, objectId, BacnetPropertyIds.PROP_PRESENT_VALUE, values)
;
这里需要注意的是,我们需要知道这个属性值的数据类型是什么,(是 float int string bool)才能写成功,如果数据类型对应不上则会失败。
List<BacnetValue> values = new List<BacnetValue>()
{
new BacnetValue (BacnetApplicationTags.BACNET_APPLICATION_TAG_REAL,
float.Parse( txt_value.Text.Trim()))
};
/// <summary>
/// 写
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnWrite_Click(object sender, EventArgs e)
{
if (treeProperty.SelectedNode == null
|| treeDevice.SelectedNode == null)
return;
BacnetAddress adr = (BacnetAddress)treeDevice.SelectedNode.Tag;
if (adr == null) return;
BacnetObjectId objectId = (BacnetObjectId)treeProperty.SelectedNode.Tag;
if (objectId == null) return;
List<BacnetValue> values = new List<BacnetValue>()
{
new BacnetValue (BacnetApplicationTags.BACNET_APPLICATION_TAG_REAL,
float.Parse( txt_value.Text.Trim()))
};
try
{
if (RWServices.m_service.WritePropertyRequest(adr, objectId,
BacnetPropertyIds.PROP_PRESENT_VALUE, values))
{
ShowText(objectId.ToString() + ": 写入成功-值:" + values[0].Value.ToString());
}
}
catch(Exception ex)
{
Log("写入失败。。" + ex.Message);
}
}
小结
介绍了Bacnet设备的读和写, 读整个设备的对象列表, 读单个对象的某个属性, 读单个对象的所有属性 ReadPropertyRequest
;写某个对象的当前值WritePropertyRequest
。
资源下载地址:https://download.csdn.net/download/weixin_40314351/89156501?spm=1001.2014.3001.5503