Reputation: 47
I'm having issues reading properties from devices on a BACNET IP router. For this I'm using .net library https://github.com/ela-compil/BACnet
If I'm interacting with multiple devices, each with its own ip and id (no router configuration), communication works and I'm able to read properties. But once a device in router mode is implemented and all other devices communicate throu this main router, I'm no longer able to read properties.
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
To read properties I'm using:
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);
This returns: "Error from device: ERROR_CLASS_SERVICES - ERROR_CODE_SERVICE_REQUEST_DENIED". If no router is used on the network, this code works and produces results. So I must be doing something wrong, while addressing the bacnet device?
Using Yabe browser all this works. So device configuration is setup corretly. Actualy devices behind router are discovered when I register foregin device throu Functions / IP Services / Foreign Device Registration.
Upvotes: 0
Views: 2416
Reputation: 1
The question is a bit older. I had the same problem. The error can be avoided if each property is queried individually. Yabe also does this if ReadProperty fails.
Code in 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;
}
}
Hope this helps some of you who end up here.
Upvotes: 0
Reputation: 488
I'm guessing that once you activate a router it might be the case that the devices have to be routed to via the router's IP(v4) address (& not the devices' IP address) - but with the addition of a combo of the appropriate DNET (Destination Network #) value & DADR (Destination Address) value to identity the device that you're targeting / e.g. trying to read properties from.
I.e. The IP address identifies the router / the device that communicates to the (Modbus?) devices, and pairing of a DNET & DADR value identifies the device that the router will assist in the communication with (- as Steven has commented).
YABE confusingly seems to display the DADR value as an IP address (rather than as a MAC address aka EUI-48/MAC-48 format); you'd have to modify YABE's source code to fix this; but if you went that route, it would also be good to trace/output the I-Am info to it's 'Log' window too - to make it easier to see/refer back to.
E.g. After the call to 'NPDU.Decode()' - having added a new 'IP_MAC' value to 'BacnetAddressTypes' (- overriding/replacing the default set value for routed-sources of "None"):
if (source != null &&
sender.Type == BacnetAddressTypes.IP)
{
source.type = BacnetAddressTypes.IP_MAC;
}
You could then add a handling 'case' statement to the 'BacnetAddress.ToString()' method, to output a more appropriate display.
When you say "communication works and I'm able to read properties" - I assume you mean when you directly plug into the devices' (MS-TP?) network; otherwise it would sound like you don't need a router (/are maybe dealing with only BACnet/IP devices).
The 'RoutedSource' is the member/property that hangs off a 'BacnetAddress' that contains the DNET & DADR values (- set within the 'NPDU.Decode()' method).
Just in case you don't know, YABE displays all the routed-to devices underneath the (relevant/parent) router device tree node, as child nodes of the router.
"DADR = ultimate destination MAC layer address"
"DLEN = 1-octet length of ultimate destination MAC layer address"
"DNET = 2-octet ultimate destination network number"
Upvotes: 0
Reputation: 354
Your devices, from the IP addresses, seem to be on different IP addresses. This is the first thing to confirm. ( 192.168.1.0 and 192.168.2.0 are normally different subnets, but this depends on your netmask settings).
As such, to 'reach across' and discover devices on other subnets, one would use a BBMD, and register with it, just as you mention with YABE, and the BBMD would distribute the discovery broadcasts (Who-Is) on the far subnet. So far, so good.
You are mixing up the concepts of "BACnet Router" and BBMD (BACnet Broadcast Management Device). In this case you should (are) using BBMD functionality, not BACnet Router functionality.
None of the above addresses your direct issue however. You seem to be creating a direct message (Using a known IP address, discovery not needed, therefore no Foreign Device registration with a BBMD required) and yet you get an error message. This seems to be an issue in the .Net implementation. What does Wireshark say was sent/received on port 47808?
Upvotes: 0