Code-House / bacnet4j-wrapper

Simple facade for bacnet4j api.
GNU General Public License v3.0
48 stars 20 forks source link

Support for BACnet server functionality #12

Open amrnablus opened 4 years ago

amrnablus commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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!

amrnablus commented 4 years ago

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 commented 4 years ago

What you propose is really neat idea. I actually did a brief attempt two years ago with d28e8382ad053d3f1c7949b0ca4ae5588f5a0698 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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

Yes, @splatch 😀

AmbroiseS commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

Thank you for the very quick response

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