读取 BACNET IP 路由器上的设备属性

问题描述 投票:0回答:3

我在从 BACNET IP 路由器上的设备读取属性时遇到问题。为此,我使用 .net 库https://github.com/ela-compil/BACnet

如果我与多个设备交互,每个设备都有自己的 IP 和 ID(无路由器配置),则通信正常并且我能够读取属性。但是,一旦实现了路由器模式下的设备并且所有其他设备都通过此主路由器进行通信,我将无法再读取属性。

Main Bacnet router IP 192.168.2.222
Device 1, Ip: 192.168.1.1, ID: 10
Device 2, Ip: 192.168.1.2, ID: 11
My software is on IP 192.168.2.220

要读取我正在使用的属性:

var targetObjId = new BacnetObjectId(BacnetObjectTypes.OBJECT_DEVICE, (uint)10);
var targetBacnetAddress = new BacnetAddress(BacnetAddressTypes.IP, "192.168.1.1");
var rez = await Bacnet_client.ReadPropertyAsync(targetBacnetAddress, targetObjId, BacnetPropertyIds.PROP_OBJECT_LIST);

这将返回:“来自设备的错误:ERROR_CLASS_SERVICES - ERROR_CODE_SERVICE_REQUEST_DENIED”。 如果网络上没有使用路由器,则此代码将起作用并产生结果。所以我在寻址 bacnet 设备时一定做错了什么?

使用 Yabe 浏览器,这一切都可以。因此设备配置已正确设置。实际上,当我通过功能/IP服务/外部设备注册注册外部设备时,会发现路由器后面的设备。

c# .net bacnet
3个回答
0
投票

从 IP 地址来看,您的设备似乎位于不同的 IP 地址。这是首先要确认的事情。 ( 192.168.1.0 和 192.168.2.0 通常是不同的子网,但这取决于您的网络掩码设置)。

因此,要“跨越”并发现其他子网上的设备,可以使用 BBMD 并向其注册,就像您在 YABE 中提到的那样,BBMD 将在远端分发发现广播 (Who-Is)子网。到目前为止,一切都很好。

您混淆了“BACnet 路由器”和 BBMD(BACnet 广播管理设备)的概念。在这种情况下,您应该使用 BBMD 功能,而不是 BACnet 路由器功能。

但是,以上都没有解决您的直接问题。您似乎正在创建一条直接消息(使用已知的 IP 地址,不需要发现,因此不需要使用 BBMD 进行外部设备注册),但您收到一条错误消息。这似乎是 .Net 实现中的一个问题。 Wireshark 说端口 47808 发送/接收了什么?


0
投票

我猜测,一旦您激活路由器,设备可能必须通过路由器的 IP(v4) 地址(而不是设备的 IP 地址)路由到 via - 但需要添加一个适当的 DNET(目标网络编号)值和 DADR(目标地址)值的组合,用于标识您要定位的设备/例如尝试从中读取属性。

即IP 地址标识路由器/与(Modbus?)设备通信的设备,DNET 和 DADR 值的配对标识路由器将协助与之通信的设备(- 正如史蒂文评论的那样)。

YABE 令人困惑地似乎将 DADR 值显示为 IP 地址(而不是 MAC 地址,即 EUI-48/MAC-48 格式);你必须修改 YABE 的源代码才能解决这个问题;但如果您走了那条路,最好也将“我是”信息跟踪/输出到它的“日志”窗口 - 以便更容易查看/参考。

例如调用“NPDU.Decode()”后 - 将新的“IP_MAC”值添加到“BacnetAddressTypes”(覆盖/替换路由源的默认设置值“None”):


    if (source != null &&
        sender.Type == BacnetAddressTypes.IP)
    {
        source.type = BacnetAddressTypes.IP_MAC;
    }

然后,您可以向“BacnetAddress.ToString()”方法添加处理“case”语句,以输出更合适的显示。

当您说“通信正常并且我能够读取属性”时 - 我假设您的意思是当您直接插入设备的(MS-TP?)网络时;否则听起来你不需要路由器(/可能只处理 BACnet/IP 设备)。

“RoatedSource”是挂起“BacnetAddress”的成员/属性,其中包含 DNET 和 DADR 值(- 在“NPDU.Decode()”方法中设置)。

以防万一您不知道,YABE 将(相关/父)路由器设备树节点下的所有路由到设备显示为路由器的子节点。

“DADR = 最终目标 MAC 层地址”

“DLEN = 最终目标 MAC 层地址的 1 个八位字节长度”

“DNET = 2 个八位字节的最终目标网络号”


0
投票

这个问题有点老了。我有同样的问题。如果单独查询每个属性,则可以避免该错误。如果 ReadProperty 失败,Yabe 也会这样做。

Yabe 中的代码(MainDialog.cs):

    private void onlyForStackoverflowExample(BacnetAddress adr, uint deviceid) 
{
    //fetch normal list
    try
    {
        if (!comm.ReadPropertyRequest(adr, new BacnetObjectId(BacnetObjectTypes.OBJECT_DEVICE, device_id), BacnetPropertyIds.PROP_OBJECT_LIST, out value_list))
        {
            Trace.TraceWarning("Didn't get response from 'Object List'");
            value_list = null;
        }
    }
    catch (Exception)
    {
        Trace.TraceWarning("Got exception from 'Object List'");
        value_list = null;
    }


    //fetch list one-by-one
    if (value_list == null)
    {
        try
        {
            //fetch object list count
            if (!comm.ReadPropertyRequest(adr, new BacnetObjectId(BacnetObjectTypes.OBJECT_DEVICE, device_id), BacnetPropertyIds.PROP_OBJECT_LIST, out value_list, 0, 0))
            {
                MessageBox.Show(this, "Couldn't fetch objects", "Communication Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, "Error during read: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        if (value_list != null && value_list.Count == 1 && value_list[0].Value is ulong)
        {
            uint list_count = (uint)(ulong)value_list[0].Value;
            AddSpaceLabel.Text = "Address Space : " + list_count.ToString() + " objects";

            // The only time sender should be a TextWriter is from the edeExport function:
            if(sender is TextWriter)
                AddObjectListOneByOne(comm, adr, device_id, list_count, AsynchRequestId);
            else
                AddObjectListOneByOneAsync(comm, adr, device_id, list_count, AsynchRequestId);

            _selectedDevice = node;
            return;
        }
        else
        {
            MessageBox.Show(this, "Couldn't read 'Object List' count", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }
    }
}

private void AddObjectListOneByOne(BacnetClient comm, BacnetAddress adr, uint device_id, uint count, int AsynchRequestId)
{
    IList<BacnetValue> value_list;
    try
    {
        for (int i = 1; i <= count; i++)
        {
            value_list = null;
            if (!comm.ReadPropertyRequest(adr, new BacnetObjectId(BacnetObjectTypes.OBJECT_DEVICE, device_id), BacnetPropertyIds.PROP_OBJECT_LIST, out value_list, 0, (uint)i))
            {
                MessageBox.Show("Couldn't fetch object list index", "Communication Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (AsynchRequestId != this.AsynchRequestId) return; // Selected device is no more the good one

            //add to tree
            foreach (BacnetValue value in value_list)
            {
                this.Invoke((MethodInvoker)delegate
                {
                    if (AsynchRequestId != this.AsynchRequestId) return;  // another test in the GUI thread
                    AddObjectEntry(comm, adr, null, (BacnetObjectId)value.Value, m_AddressSpaceTree.Nodes);
                });
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error during read: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }
}

希望这对最终来到这里的人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.