ChristianTremblay / BAC0

BAC0 - Library depending on BACpypes3 (Python 3) to build automation script for BACnet applications
GNU Lesser General Public License v3.0
173 stars 99 forks source link

Communicating across a different subnetwork. #167

Closed camfeghali closed 4 years ago

camfeghali commented 5 years ago

Hey Christian, I'm trying to read/write to a Bacnet supervisory controller which is on a different subnetwork than the raspberryPi where we're running BAC0.

Q1: Is BACnet router supported ? ( We only see bbmd recommended ) Q2: Is there a way to change the network ID ? The default seems to be 1001.

ChristianTremblay commented 5 years ago

Hi @camfeghali , when using BACnet you need to make the difference between requests relying on broadcasts (like whois, iam, etc) and other requests using unicasts. Broadcast requests can be global (255.255.255.255) or local (let say 192.168.1.255 for example). Typically, switches will not allow broadcast to traverse them... they will stay "local" to the subnet. Unicats requests are made to point a specific IP.

BBMDs are special devices (one per subnet) that will sit on a subnet and intercept broadcast messages, packet them into unicast messages and send them to known devices (BBMD or registered foreign devices) in another subnet. This way, other subnets (or foreign devices) will see broadcast messages and they will be able to respond (by an IAM for example).

BACnet routers are typically IP to MSTP or IP to IP... They will make BACnet messages change their physical transport layer. Not sure how this will help you... except if you use one router as a BBMD.

The network ID (we call that network "number") must be unique on a complete BACnet network. And some rules apply. If you see 1001, it's because one device sitting on it declared it. Router or other (Tridium Jaces for example) can be configured to declare the network number. But you need to keep it unique on the network.

That said, if you don't need to use broadcast requests and you know where to point your request, there is no need for BBMDs as you can build unicast request to a particular controller.

For example, I can easily setup a BAC0 device over the web if BACnet UDP port is exposed. A serious security issue, yes, but possible. Because when defining a device, BAC0 will build its requests with the IP address of the device and make a unicast message to it.

You could see that using a simple analogy of people in a room. Wois and IAM, you yell at everybody in the room and everybody can yell back at you to respond. If you need to see who's in the room next to you, you wil need someone that will listen to all your yelling and this person will call somebody else in the other room to tell him the requests...

But there is nothing that prevent you to go directly to the person to whom you wish to talk (unicast). And talk instead of yelling.

But for that, yes, you need to know who's in the room first. Which can be found in the control drawing of a project normally.

Hope I understood the question well and the answer is clear enough.

camfeghali commented 5 years ago

Hey @ChristianTremblay, thank you so much, that's very helpful.

When doing BAC0.connect(...), we couldn't figure out how to specify our number to be. (The bac0 device gave itself 1001). when initializing BAC0 is there a place to tell it what network number to assign to itself? or are you saying that assignment happens externally by a controller?

Also, how do we make a unicast call without previously discovering the device? lets say our bac0 device is on 192.168.1.*, and the bacnet device is on 192.168.2.*and has network number 5001. In my tests all of the calls that worked started with the network number, e.g. bacnet.read('5001 ...') (done when the two devices were on the same network) but if we didn't previously discover 5001, how do we tell it the ip address to find 5001? is there an in-memory map somewhere that can be managed directly?

ChristianTremblay commented 5 years ago

BAC0 itself do not need self awareness of its network number. By defining BAC0.connect() or BAC0.lite() the script itself, will become a BACnet device residing on the IP network. Network number also only make sense if another device, on another network number, would need to communicate with BAC0. A rare case.

For your 2nd question, BAC0 counts on bacpypes for all network requests. The routing part is handled by bacpypes. I woudl start by asking : what is the device on .2.* ?

If it's a device, you could technically connect to it direclty. No need to provide the network number.

If you need to access MSTP devices, on network 5001, which are under a router on .2.* things are different. BAC0 (bacpypes in fact) need to know "who is router to network 5001".

Which is a request you can make :

bacnet.whois_router_to_network('5001')

This kind of request will help bacpypes figure out the "map" of the network and allow it to see what is on the network.

To help, I recently created the bacnet.discover() routine. It will throw a bunch of requests to the network and try to dig its way down.

If this doesn't work, you may need to declare BAC0 as a Foreign device but for that, you need one device on .2.* to be defined as a BBMD.

This way, broadcast requests will find their way to BAC0 and devices will be reachable.

Does it help ?

camfeghali commented 5 years ago

Yes, thank you very much for your time.

camfeghali commented 5 years ago

The problem we are facing with discover is that it would still require it to be "discoverable", like on the same network. Given the most extreme example of two bacnet devices (one of which is BAC0) who are both on the public internet, in two completely different locations, bacnet.whois_router_to_network('5001') would not be able to discover them (it would not traverse the entire public internet).

Lets say, given a bacnet/ip device on the public internet, how does one make a unicast request? you would have to hard-code the bacnet device’s public IP address somewhere, and we cannot find a clear example of how to do this.

Hopefully there is not something that we are fundamentally misunderstanding about all of this, but its starting to feel that way lol.

Again, thank you so much for taking the time to break this down.

ChristianTremblay commented 5 years ago

Thinking about it make me realize that there is quite a great improvement I can do to whois_router_to_network In the next release, which should be fast (I hope) I will change the function to accept a "destination" parameter. This will allow discovery of network which are not "visible" in the subnet directly.

In fact, I'll try to add this feature to whois, and others... stay tuned

camfeghali commented 5 years ago

Hey Christian, thanks again for taking the time to answer my questions.

Is there a way of defining points on my BAC0 instance ? That could be read by another BACnet device on the network ?

Tyvm again!

ChristianTremblay commented 5 years ago

Absolutely, I created a gist of a Meteo device I'm working on : https://gist.github.com/ChristianTremblay/9bbd655775982730e62e538e44512771

The core is not provided but you will see how I create the device