Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for BACnet server functionality #12

Open
amrnablus opened this issue Mar 2, 2020 · 12 comments
Open

Support for BACnet server functionality #12

amrnablus opened this issue Mar 2, 2020 · 12 comments

Comments

@amrnablus
Copy link

Hello and thanks for your great work,
I was wondering if you have any example of building a bacnet server with writable properties.
Thanks

@splatch
Copy link
Member

splatch commented Mar 3, 2020

Hey @amrnablus,
Library currently serves a client facade and doesn't do anything related to hosting server functionalities. Over 4 years ago when I first attempted to do it with bacnet4j it was quite bad and I couldn't find any working code samples. I must say that it is improved, unit tests provide decent amount of samples.

Inspired by your inquiry I made a small test and managed to get it running. See code below.

// Free sample, use it at your own risk. Remember bacnet4j is licensed under GPL.
package com.connectorio.bacnet;

import java.util.List;
import java.util.Random;
import com.serotonin.bacnet4j.LocalDevice;
import com.serotonin.bacnet4j.RemoteDevice;
import com.serotonin.bacnet4j.npdu.ip.IpNetwork;
import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder;
import com.serotonin.bacnet4j.obj.AnalogInputObject;
import com.serotonin.bacnet4j.transport.DefaultTransport;
import com.serotonin.bacnet4j.transport.Transport;
import com.serotonin.bacnet4j.type.enumerated.EngineeringUnits;
import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier;
import com.serotonin.bacnet4j.type.primitive.Real;

/**
 * Sample bacnet4j server which exposes data over BACnet/IP.
 *
 * @author Łukasz Dywicki <luke@code-house.org>
 */
public class Main {
    public static void main(String[] args) throws Exception {

        IpNetwork network = new IpNetworkBuilder().withBroadcast("x.y.z.w", 24).build();
        Transport transport = new DefaultTransport(network);
        transport.setTimeout(500000);
        transport.setSegTimeout(15000);
        final LocalDevice localDevice = new LocalDevice(11338, transport);

        AnalogInputObject aio1 = new AnalogInputObject(localDevice, 1, "test1", 1000.0f, EngineeringUnits.wattHours,
                false);

        final int min = 10, max = 35;

        Thread simulation = new Thread(new Runnable() {
            @Override public void run() {
                while (true) {
                    float random = new Random().nextFloat();
                    float value = min + random * (max - min);

                    aio1.writePropertyInternal(PropertyIdentifier.presentValue, new Real(value));
                }
            }
        }, "simulation");
        simulation.setDaemon(true);
        simulation.start();

        localDevice.initialize();

        System.in.read();
        localDevice.terminate();
    }

}

Below is a proof it actually works as designed:
yabe-bacnet4j

Enjoy!

@amrnablus
Copy link
Author

Thanks @splatch, that's pretty much what i did, the tricky part was accepting writes from YABE to my backend server, i ended up with something like this:

public class ThermostatAdapeter extends DeviceEventAdapter ....
public class ThermostatEventListener ...
ThermostatAdapeter listener = new ThermostatAdapeter(executor, new ThermostatEventListener());

masterDevice.getEventHandler().addListener(listener);

My adapter gets triggered whenever there is a change on the properties.

That said, if you plan on wrapping the server code let me know, i might be able to contribute to that.

@splatch
Copy link
Member

splatch commented Mar 3, 2020

Thanks for feedback and contribution proposal. Having server functionality is definitely interesting, but currently not yet in roadmap, if you don't mind I will mark issue as feature request.

In some undefined future I hope to port this facade to rely on BACnet support provided by Apache PLC4X project. It has Apache license by default, uses netty and NIO making at least java part closer to usual routine. It is also a bit more complex in some areas as it does more things than just a building automation. Currently Apache PLC4X offers support for "passive" mode - meaning replaying PCAPs with bacnet traffic, but I made my first experiments with live connection which prove that library, witch some glitches, is working. Obviously it is fresh and lacks many of bacnet4j features such as segmentation, server side handling, but it also has certain advantages coming from young codebase.

Here is abstract notation of protocol frames (messages):
https://github.com/apache/plc4x/blob/release/0.6.0/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec

And here is brief logic of passive handling of protocol (notice this is available in develop/snapshot version):
https://github.com/apache/plc4x/blob/7f8e033de7ea5dc48f3811c273f5e8a7b3d5175a/sandbox/test-java-bacnetip-driver/src/main/java/org/apache/plc4x/java/bacnetip/protocol/PassiveBacNetIpProtocolLogic.java#L104

Please feel yourself invited to plc4x community!

@splatch splatch changed the title Sample server code Support for BACnet server functionality Mar 3, 2020
@amrnablus
Copy link
Author

Interesting, if i may suggest, why not create a generic wrapper with backend options being bacnet4j or plc4x (kinda like JPA but for bacnet).

Either way i'll keep an eye on the work here and i'll be happy to help.

@splatch
Copy link
Member

splatch commented Mar 3, 2020

What you propose is really neat idea. I actually did a brief attempt two years ago with d28e838 commit (2.0.x branch). Overall idea was to bring ASLv2 API and use BACnet4J as an implementation detail.
Given current circumstances it makes even more sense now to bring this idea back.

@amrnablus
Copy link
Author

Yep, that looks exactly like what i have in mind, we can start by writing a generic interface which later a facade can be written for each provider in order to have it be "JBA (Java Bacnet API)?" compatible

@splatch
Copy link
Member

splatch commented Mar 4, 2020

JBA sounds awesome. I like it a lot because it will not confuse people who come from Niagara/Tridium and know what their BAJA shortcut stands for (Building Automation Java Architecture).

@splatch
Copy link
Member

splatch commented Mar 4, 2020

Hey @baardl, would you be interested in such functionality for your experiments? This will allow to make a bridge between your lab devices and rest of existing 0xBAC0 infrastructure in the building.

@baardl
Copy link

baardl commented Mar 5, 2020

Yes, @splatch 😀

@AmbroiseS
Copy link

Hello,

I am also implementing a bacnet server.
Current tests are going ok.

I am wondering what these values do :
transport.setTimeout(500000);
transport.setSegTimeout(15000);

  1. Will the transport shutdown after 500000 (No idea what unit) ?
  2. Do you know if plc4x provides bacnet server impl now ?

Thank you for any help

@splatch
Copy link
Member

splatch commented Oct 16, 2023

I can't remember exactly why these values got in. Timeout is in ms (I suppose), its mostly there for debugging purposes because transport thread/threads are being used to dispatch events to callbacks you implement. Segmentation timeout is probably in seconds and its extra long for that reason too. You should be fine without these values or reverted to reasonable levels.

The plc4x work is a bit forward from where it was, however its still not even close to bacnet4j. Later one is full blown sdk, whereas plc4x is still at the library level for bacnet. If you look closer at plc4x sources, there is updated code which answers for bacnet whois requests, but not much more than that. Also, there is very little structuring behind (i.e. AnalogInputObjects), making amount of work much larger. Yet leaving you a lot of flexibility of how to handle entire thing.

@AmbroiseS
Copy link

Thank you for the very quick response

I'll use default values for my testing.
And keep my bacnet4j implementation also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants