connect to Bacnet device with java using bacnet4j

290 views Asked by At

I am trying to write simple code to read a simulator yabe device in my Java code. I can read the device with yab (a bacnet explorer free app) I am missing something I cant get what

     String hostAddress="192.168.1.9";
        IpNetwork network = new IpNetworkBuilder().withPort(47808).withSubnet(hostAddress, 24).build();
        Transport transport = new DefaultTransport(network);
        LocalDevice localDevice = new LocalDevice(3227671, transport);
        localDevice.initialize();
        localDevice.sendGlobalBroadcast(new WhoIsRequest());

        int remoteId=123;
        List<RemoteDevice> remoteDevices = localDevice.getRemoteDevices(); 

the list is always empty. this is the simulato device:

enter image description here

can anyone direct me what am I missing? Thank you.

2

There are 2 answers

0
DennisVM-D2i On

Forewarning: I'm not a BACnet4J developer nor a Java developer; and I'm using Windows 10.

To start with, you could add 'SLF4J' ('reload4') to get a small sense of what is occurring, e.g. something like so (- having got the libraries online):

-classpath %CD%;Lib\bacnet4j-3.2.2.jar;Lib\slf4j-api-1.7.36.jar;Lib\commons-lang3-3.12.0.jar;Lib\reload4j-1.2.19.jar;Lib\slf4j-reload4j-1.7.36.jar

E.g. With the 'log4j.properties' configured:

log = Logs

# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=${log}/Log.txt

# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%d %m%n

And at the beginning of your 'main()' method - to be sure you've configured the logging properly:

            Logger logger = LoggerFactory.getLogger(Main.class);

            logger.info("- Main() -");

You can also (install &) run Wireshark (as Administrator), and put a 'capture filter' of something like 'udp portrange 47808-47823' to see the BACnet/IP traffic that is flowing.

Now more so to your question.

After the device's 'initialize()', you probably need a 'Listener' class to listen out for the 'I-Am's being received:

            localDevice.getEventHandler().addListener(new Listener());

With listener class that is something remotely like this:

    static class Listener extends DeviceEventAdapter
    {
        @Override
        public void iAmReceived(RemoteDevice d)
        {
            try
            {
                System.out.println("IAm received from " + d);
                System.out.println("Segmentation: " + d.getSegmentationSupported());
                d.setSegmentationSupported(Segmentation.noSegmentation);

                Address a = new Address(new Unsigned16(0), new OctetString(new byte[] { (byte) 0xc0, (byte) 0xa8, 0x1,
                        0x5, (byte) 0xba, (byte) 0xc0 }));

                System.out.println("Equals: " + a.equals(d.getAddress()));
                getExtendedDeviceInformation(d);
                System.out.println("Done getting extended information");

                List oids = ((SequenceOf) RequestUtils.sendReadPropertyAllowNull(localDevice, d,
                        d.getObjectIdentifier(), PropertyIdentifier.objectList)).getValues();
                System.out.println(oids);
            }
            catch (BACnetException e)
            {
                e.printStackTrace();
            }
        }
    }

    static void getExtendedDeviceInformation(RemoteDevice d)
        throws BACnetException
    {
        ObjectIdentifier oid = d.getObjectIdentifier();

        ReadPropertyAck ack;

        // -

        System.out.println("ExtDevInfo:  Getting 'Object-Name' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.objectName)).get()
                );

        d.setName(ack.getValue().toString());

        // -

        System.out.println("ExtDevInfo:  Getting 'Vendor-Identifier' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.vendorIdentifier)).get()
                );

        d.setVendorId(((Unsigned16) ack.getValue()).intValue());

        // -

        System.out.println("ExtDevInfo:  Getting 'Vendor-Name' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.vendorName)).get()
                );

        d.setVendorName(ack.getValue().toString());


        // -

        System.out.println("ExtDevInfo:  Getting 'Protocol-Version' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolVersion)).get()
                );

        d.setProtocolVersion((UnsignedInteger) ack.getValue());

        // -

        System.out.println("ExtDevInfo:  Getting 'Protocol-Revision' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolRevision)).get()
                );

        d.setProtocolRevision((UnsignedInteger) ack.getValue());

        // -

        // Get the device's supported services
        System.out.println("ExtDevInfo:  Getting 'Protocol-Services-Supported' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolServicesSupported))
                ).get();

        d.setServicesSupported((ServicesSupported) ack.getValue());

        // -

        System.out.println();
    }

But I'm guessing that this framework, will not fire the listener's 'iAmReceived()' method, if it's for the same device, so you would either need to define another 'device' (/might be good to try as an alternative/easier? starting-point test), or as you've done try using another device (- unless I'm wrong I'm not sure why this API does not appear to have a 'client'-only class / you seem to have to use a 'device' as being the client).

But I'm guessing the problem with you set-up, might be the (local/'255.255.255.255') 'Who-Is' broadcast, whereby it's only going to broadcast to the local subnet, and to devices that are using the same port #; so in terms of orchestrating that, you'd probably be better off using the IPv4 (only) range of addresses (- IPv6 is cack in this regard in that they've reduced the localhost to being only 1 address rather than like IPv4 a localhost range of addresses or even 5-10 addresses?!), so you can have both the client and server device listening upon the same (Class 'A') network but with the same port #.

Ignoring the fact that I seem to have a newer(?) version of the BACnet4J library, i.e. method-names differences:

            IpNetwork network =
                new IpNetworkBuilder()
                        .localBindAddress("127.0.0.1")
                        .port(47810)
                        .subnetMask(hostAddress)
                        .build();

E.g. Possibly something close to what could work for two local host addresses:

// https://repo1.maven.org/maven2/org/slf4j/slf4j-reload4j/1.7.36/

import java.util.List;

import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.RemoteDevice;
import com.serotonin.bacnet4j.event.DeviceEventAdapter;
import com.serotonin.bacnet4j.exception.BACnetException;
import com.serotonin.bacnet4j.exception.BACnetTimeoutException;
// import com.serotonin.bacnet4j.npdu.Network;
import com.serotonin.bacnet4j.npdu.ip.IpNetwork;
import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder;
// import com.serotonin.bacnet4j.npdu.ip.IpNetworkUtils;
import com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyAck;
import com.serotonin.bacnet4j.service.confirmed.ReadPropertyRequest;
import com.serotonin.bacnet4j.service.unconfirmed.WhoIsRequest;
import com.serotonin.bacnet4j.transport.DefaultTransport;
import com.serotonin.bacnet4j.transport.Transport;
import com.serotonin.bacnet4j.type.constructed.Address;
import com.serotonin.bacnet4j.type.constructed.SequenceOf;
import com.serotonin.bacnet4j.type.constructed.ServicesSupported;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.enumerated.Segmentation;
import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier;
import com.serotonin.bacnet4j.type.primitive.OctetString;
import com.serotonin.bacnet4j.type.primitive.Unsigned16;
import com.serotonin.bacnet4j.type.primitive.UnsignedInteger;
import com.serotonin.bacnet4j.util.RequestUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class Main
{
    private static LocalDevice localDevice;

    public static void main(String[] args)
        throws Exception
    {
        try
        {
            Logger logger = LoggerFactory.getLogger(Main.class);

            logger.info("- Main() -");

            String hostAddress = "127.255.255.255";
            //String hostAddress = "192.168.0.255";
            //String hostAddress = "192.168.56.255";
            //String hostAddress = "192.168.255.255";

            IpNetwork network =
                new IpNetworkBuilder()
                        .localBindAddress("127.0.0.1")
                        .port(47810)
                        .subnetMask(hostAddress)
                        .build();

            Transport transport = new DefaultTransport(network);

            localDevice = new LocalDevice(3227671, transport);
            localDevice.initialize();

            localDevice.getEventHandler().addListener(new Listener());

            localDevice.sendGlobalBroadcast(new WhoIsRequest());

            int remoteId = 999;
            List<RemoteDevice> remoteDevices = localDevice.getRemoteDevices();

            Thread.sleep(3000);

            RemoteDevice d = remoteDevices.get(0);

            ObjectIdentifier oid = d.getObjectIdentifier();

            ReadPropertyAck ack;

            // -

            System.out.println("ExtDevInfo:  Getting 'Object-Name' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.objectName)).get()
                    );

            d.setName(ack.getValue().toString());

            // -

            System.out.println("ExtDevInfo:  Getting 'Vendor-Identifier' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.vendorIdentifier)).get()
                    );

            d.setVendorId(((Unsigned16) ack.getValue()).intValue());

            // -

            System.out.println("ExtDevInfo:  Getting 'Vendor-Name' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.vendorName)).get()
                    );

            d.setVendorName(ack.getValue().toString());


            // -

            System.out.println("ExtDevInfo:  Getting 'Protocol-Version' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.protocolVersion)).get()
                    );

            d.setProtocolVersion((UnsignedInteger) ack.getValue());

            // -

            System.out.println("ExtDevInfo:  Getting 'Protocol-Revision' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.protocolRevision)).get()
                    );

            d.setProtocolRevision((UnsignedInteger) ack.getValue());

            // -

            // Get the device's supported services
            System.out.println("ExtDevInfo:  Getting 'Protocol-Services-Supported' .  .   .");

            ack =
                (ReadPropertyAck)
                    (
                        localDevice.send(
                            d,
                            new ReadPropertyRequest(
                                oid,
                                PropertyIdentifier.protocolServicesSupported))
                    ).get();

            d.setServicesSupported((ServicesSupported) ack.getValue());

            // -

            System.out.println();
        }
        catch (BACnetTimeoutException exc)
        {
            System.out.flush();

            System.err.println(exc.getMessage());
            System.err.flush();
        }
        catch (BACnetException exc)
        {
            System.out.flush();

            System.err.println(exc.getMessage());
            System.err.flush();
        }
        catch (Exception exc)
        {
            System.out.flush();

            System.err.println(exc.getMessage());
            System.err.flush();
        }
        finally
        {
            localDevice.terminate();

            System.out.println();
        }
    }

    static class Listener extends DeviceEventAdapter
    {
        @Override
        public void iAmReceived(RemoteDevice d)
        {
            try
            {
                System.out.println("IAm received from " + d);
                System.out.println("Segmentation: " + d.getSegmentationSupported());
                d.setSegmentationSupported(Segmentation.noSegmentation);

                Address a = new Address(new Unsigned16(0), new OctetString(new byte[] { (byte) 0xc0, (byte) 0xa8, 0x1,
                        0x5, (byte) 0xba, (byte) 0xc0 }));

                System.out.println("Equals: " + a.equals(d.getAddress()));

                return;
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    static void getExtendedDeviceInformation(RemoteDevice d)
        throws BACnetException
    {
        ObjectIdentifier oid = d.getObjectIdentifier();

        ReadPropertyAck ack;

        // -

        System.out.println("ExtDevInfo:  Getting 'Object-Name' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.objectName)).get()
                );

        d.setName(ack.getValue().toString());

        // -

        System.out.println("ExtDevInfo:  Getting 'Vendor-Identifier' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.vendorIdentifier)).get()
                );

        d.setVendorId(((Unsigned16) ack.getValue()).intValue());

        // -

        System.out.println("ExtDevInfo:  Getting 'Vendor-Name' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.vendorName)).get()
                );

        d.setVendorName(ack.getValue().toString());


        // -

        System.out.println("ExtDevInfo:  Getting 'Protocol-Version' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolVersion)).get()
                );

        d.setProtocolVersion((UnsignedInteger) ack.getValue());

        // -

        System.out.println("ExtDevInfo:  Getting 'Protocol-Revision' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolRevision)).get()
                );

        d.setProtocolRevision((UnsignedInteger) ack.getValue());

        // -

        // Get the device's supported services
        System.out.println("ExtDevInfo:  Getting 'Protocol-Services-Supported' .  .   .");

        ack =
            (ReadPropertyAck)
                (
                    localDevice.send(
                        d,
                        new ReadPropertyRequest(
                            oid,
                            PropertyIdentifier.protocolServicesSupported))
                ).get();

        d.setServicesSupported((ServicesSupported) ack.getValue());

        // -

        System.out.println();
    }
}

0
SimpleJack On

The answer from DennisVM-D2i is absolutely fine for his version (v.3.2.2), but BACnet4J has improved in simplicity since then. Today the library does all the heavy lifting for you. Sadly the documentation has NOT improved & is still almost none existing which finally leads to this StackOverflow question...

NOTE: I'm using BACnet4J v.6.0.0 from the Sonatype Repository

public static void main(String[] args) throws Exception {
    // Grab your current local address
    String localAddress = InetAddress.getLocalHost().getHostAddress();
    // Build new BACnet device on homeaddress (NOT localhost) that was given from your router. Do the broadcast on same homeaddress on port 47808 (this is where the Room Controll Simulator defaults to)
    IpNetworkBuilder builder = new IpNetworkBuilder().withLocalBindAddress(localAddress).withSubnet(localAddress, 24).withPort(47808);
    IpNetwork network = builder.build();
    // New BACnet device gets ID: 12345
    LocalDevice localDevice = new LocalDevice(12345, new DefaultTransport(network));
    try {
        localDevice.initialize();
        RemoteDeviceDiscoverer discoverer = localDevice.startRemoteDeviceDiscovery();
        discoverer.start();

        // Wait for responses for 3 seconds...
        TimeUnit.SECONDS.sleep(3);

        discoverer.stop();

        if(discoverer.getRemoteDevices().isEmpty()) {
            System.out.println("FOUND NO DEVICES!");
        } else {
            for (RemoteDevice device : discoverer.getRemoteDevices()) {
                System.out.println("FOUND: " + device);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // Cleanup
        localDevice.terminate();
        network.terminate();
    }
}

Look out for more, already implemented utility classes in the official GitHub Repo:

enter image description here