ChristianTremblay / BAC0

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

how to write a proprietary objects' property? #233

Closed JerryXinhui closed 3 years ago

JerryXinhui commented 3 years ago

I'm now using BAC0 to do my automation test, and I really find it is a fantastic python lib. But I have read all the document, but I still have no ideal how to write a proprietary objects' property. How can I do that? image

NubeDev commented 3 years ago

Are you trying to use BAC0 as a BACnet master or as a BACnet server?

JerryXinhui commented 3 years ago

I use BAC0 to read/write my controller, so I think it is a BACnet server. My code maybe like this: import BAC0 deviceIP = "192.168.251.119" # this is my controller localIP = "192.168.251.202" bacnet = BAC0.connect(localIP) bacnet.read(deviceIP + " device " + str(device_id) + " objectList")

ChristianTremblay commented 3 years ago

BACnet do not use the notion of client and server a lot... there are BACnet devices. Most of the time, BACnet devices have "objects" that can be read/written to by other devices.

BAC0 is not different as it is a BACnet device that will be seen on a network. BAC0 can have local objects that could be read/ written to by other devices. BAC0 can read from and write to other devices.

Local objects have to be added to your code depending on your use case (a gateway of some kind... or a weather app let say). But I don't think you are referring to this use case.

When using BAC0 to interact with other devices to read from them and write to them (which is the case I think you are trying to build). Normally, it will be easy for typical points.

But when the manufacturer include "local objects" in their device that are proprietary objects, things become a little more complicated.

Reading proprietary objects is the simplest case. Let say you have a proprietary object named 142 with an instance number of 1. And you want to read the presentValue of this object. You can use

bacnet.read('address @obj_142 1 presentValue')

Manufacturer can also provide proprietary "properties" to an object. For example, JCI adds properties 603 to the deviceObject to give the status of the end of line on the controller... One could use

bacnet.read('address device devInstance @prop_603')

Now, what if you want to write to a proprietary object or property ? You need to know what "kind of data" you are allowed to write. Real ? Boolean ? Enumerated ? Unsigned ? This is where you will need some work. Trials an error, guesses and research. Because guess what ? Manufacturer will probably not share all that data.

Once you know what you have to write, you need to create a class that will define the new object and/or properties. You will attach this class to a vendor_id. Register that new class to bacpypes and when you will use bacnet.write, passing the vendor_id, bacpypes will know that the new class must be use and it will know how to handle a write request.

You can see an example here : https://github.com/ChristianTremblay/BAC0/blob/master/BAC0/core/proprietary_objects/jci.py https://github.com/ChristianTremblay/BAC0/blob/master/BAC0/core/proprietary_objects/__init__.py

JerryXinhui commented 3 years ago

@ChristianTremblay thank you for your quick reply. I have seen your solution before. In the solution, all the proprietary object actually is a object which contain proprietary property. What if my controller have a real proprietary object which Object_Identifier is bigger than 50. It's neither a AnalogValueObject or AnalogInputObject. Like how I can write a proprietary object named 142? I hope I make my question clearly.

JerryXinhui commented 3 years ago

What value should I put in the bacpypes_type if my object identifier is 142?

TestDeviceObject = {
    "name": "TestObject",
    "vendor_id": 5,
    "objectType": "142",
    "bacpypes_type": "???",
    "properties": {
        "testproperty": {"obj_id": 4874, "primitive": Boolean, "mutable": False},

    },
}
ChristianTremblay commented 3 years ago

This is where you need to make research....

In [13]: bacnet.readMultiple('{} @obj_142 1 all'.format(d_ip))
Out[13]: 
[{'142_objectIdentifier': (142, 1)},
 {'142_objectType': 142},
 {'142_objectName': 'Data Exchange Settings 580000'},
 {'142_1032': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]},
 {'142_presentValue': True},
 {'142_1034': False},
 {'142_description': ''},
 {'142_1036': 2},
 {'142_1037': 22},
 {'142_1038': 30},
 {'142_1039': 74375323},
 {'142_1040': 0},
 {'142_1041': 5},
 {'142_1042': 30},
 {'142_1043': 74375153},
 {'142_1044': 0},
 {'142_1046': <bacpypes.constructeddata.Any at 0x1de952a8848>},
 {'142_1055': 50},
 {'142_1056': 1800},
 {'142_1057': 74376503},
 {'142_1058': 0},
 {'142_1059': 90},
 {'142_1060': 74375863},
 {'142_1061': 15},
 {'142_1062': 1},
 {'142_1064': <bacpypes.constructeddata.Any at 0x1de953d7cc8>},
 {'142_statusFlags': [0, 0, 0, 0]},
 {'142_1066': True},
 {'142_1068': <bacpypes.constructeddata.Any at 0x1de953d7608>},
 {'142_1084': <bacpypes.constructeddata.Any at 0x1de953d79c8>}]

142_presentValue is True, so we can think it's binary... you can probably try BinaryValueObject... but nothing sure here...

To define each proprietary properties... same kind of research...but good luck with constructed data.

JerryXinhui commented 3 years ago

aha, I get your point and find the reason. Because I defined as this "objectType": "142", so when I write this proprietary object, the write.py convert the objectType to int 142, in the register class container, the register class is string type 142, so the project pop an error, shows "no such objectType find". Many thanks to you!