switching from bacnet4j3 to bacnet4j4 using special ObjectProperties
-
Hello community,
I'm searching for an example how to use the old ObjectProperties.addPropertyTypeDefinition in bacnet4j4 now. I don't know how to add new ObjectProperties.
In the past ObjectProperties could be defined like this:
ObjectProperties.addPropertyTypeDefinition(ObjectType.lifeSafetyZone, PropertyIdentifier.forId(MY_SPECIAL_VALUE), CharacterString.class, true, false);
Now the add- function is private, so bacnet4j cannot handle answers for the PropertieIdentifier with the value of MY_SPECIAL_VALUE. Because of this the Objectpropertie is undefined. RequestUtils.readProperties causes failures in parsing the ComplexAck.
RequestUtils.readProperties(localDevice, d, oprs, null);
with oprs
final List<ObjectPropertyReference> oprs = new ArrayList<>(); oprs.add(new ObjectPropertyReference(oId, PropertyIdentifier.objectName)); oprs.add(new ObjectPropertyReference(oId, PropertyIdentifier.description)); oprs.add(new ObjectPropertyReference(oId, PropertyIdentifier.forId(MY_SPECIAL_VALUE)));
Stack-Trace:
Caused by: com.serotonin.bacnet4j.exception.BACnetException: property: missing-required-parameter at com.serotonin.bacnet4j.transport.ServiceFutureImpl.result(ServiceFutureImpl.java:76) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.transport.ServiceFutureImpl.get(ServiceFutureImpl.java:63) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.util.RequestUtils.readProperties(RequestUtils.java:313) ~[bacnet4j-4.1.6.jar:?] ... 13 more Caused by: com.serotonin.bacnet4j.exception.BACnetErrorException: property: missing-required-parameter at com.serotonin.bacnet4j.type.Encodable.popEnd(Encodable.java:186) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.primitive.Primitive.createPrimitive(Primitive.java:67) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.Encodable.readANY(Encodable.java:368) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.Choice.read(Choice.java:102) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.Choice.<init>(Choice.java:75) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.Encodable.readChoice(Encodable.java:267) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.ReadAccessResult$Result.<init>(ReadAccessResult.java:141) ~[bacnet4j-4.1.6.jar:?] at sun.reflect.GeneratedConstructorAccessor45.newInstance(Unknown Source) ~[?:?] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_131] at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[?:1.8.0_131] at com.serotonin.bacnet4j.type.Encodable.read(Encodable.java:230) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.SequenceOf.<init>(SequenceOf.java:87) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.Encodable.readSequenceOf(Encodable.java:309) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.Encodable.readOptionalSequenceOf(Encodable.java:342) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.ReadAccessResult.<init>(ReadAccessResult.java:75) ~[bacnet4j-4.1.6.jar:?] at sun.reflect.GeneratedConstructorAccessor48.newInstance(Unknown Source) ~[?:?] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) ~[?:1.8.0_131] at java.lang.reflect.Constructor.newInstance(Unknown Source) ~[?:1.8.0_131] at com.serotonin.bacnet4j.type.Encodable.read(Encodable.java:230) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.constructed.SequenceOf.<init>(SequenceOf.java:74) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.type.Encodable.readSequenceOf(Encodable.java:298) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyMultipleAck.<init>(ReadPropertyMultipleAck.java:56) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.service.acknowledgement.AcknowledgementService.createAcknowledgementService(AcknowledgementService.java:52) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.apdu.ComplexACK.parseServiceData(ComplexACK.java:211) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.transport.DefaultTransport.completeComplexAckResponse(DefaultTransport.java:757) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.transport.DefaultTransport.receiveAPDU(DefaultTransport.java:679) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.transport.DefaultTransport.receiveImpl(DefaultTransport.java:573) ~[bacnet4j-4.1.6.jar:?] at com.serotonin.bacnet4j.transport.DefaultTransport.run(DefaultTransport.java:493) ~[bacnet4j-4.1.6.jar:?] at java.lang.Thread.run(Unknown Source) [?:1.8.0_131]
Wireshark of this special property:
Is someone able to show me a way to fix my problem?Many thanks in advance,
Bernd
-
Did you find a solution? I have exactly the same problem.
-
I believe the intention, but I can't speak for the author, is that if you've got custom properties you should be overriding the appropriate classes (ObjectProperties, PropertyIdentifier, ObjectType, etc) with classes modified to support your situation.
-
Not so easy, PropertyIdentifier, that is the most important for me, has private constructor.
-
And
ObjectProperties.add()
has private access incom.serotonin.bacnet4j.obj.ObjectProperties
.Making a gateway I need to map a proprietary point attributes to BACnet point properties. However, some of the attributes do not have a standard equivalent on the BACnet side. For example, we have multiple alarm levels on the analog value object (HiLevel1=warning, HiLevel2=alarm, HiLevel3=fault), etc. I really did not find any way how to add proprietary (custom) properties to a standard BACnet object except to modify the original code.
-
Correct.
Those classes are for the most part large enumerations such that modifying them would be straightforward.
-
I found a way, how to add my own proprietary identifier! The static method
PropertyIdentifier.forId
adds the new id into the idMap when the id does not exist. Unfortunatelly, the new property name is not added into the nameMap and prettyMap, so I added the names directly.public class MyPropertyIdentifier extends PropertyIdentifier { public static final PropertyIdentifier myProperty = PropertyIdentifier.forId(2048); public MyPropertyIdentifier(ByteQueue queue) throws BACnetErrorException { super(queue); } static { // Enumerated.init(MyPropertyIdentifier.class, idMap, nameMap, prettyMap); getNameMap().put("my-property", myProperty); getPrettyMap().put(2048, "my-property"); } }
However, adding the new property identifier to an object is not possible because the add methods are private. The library, without source code modifications, is not conform with the chapter 23 of the BACnet specification.
-
I should reiterate that I'm not the original author so I'm speculating on both the proper way to add objects and the intention.
I just read chapter 23 of ASHRAE-D-86451 for the first time but found nothing about extending BACnet being a runtime feature, or something that should only require API calls. Seems more intended for people implementing things in devices, no? Nevertheless, one can see that enabling runtime modification of the ObjectProperties would violate the stipulation that should a vendor define proprietary objects they must also have a registered vendor ID with ASHRAE such that your enumerations of properties are not unknowable for interoperability, was my reading.
Use of BACnet4J without modifying the source will give you a vendor ID associated with Infinite Automation Systems too, see https://github.com/infiniteautomation/BACnet4J/blob/master/src/main/java/com/serotonin/bacnet4j/obj/DeviceObject.java
But if you really want to commit to runtime modification, you could deviate from the spec and reflect the add method out of being private, but I don't think that's the right thing to do. You'll have to do something to not present our vendor ID on the device object, though, since your proprietary objects are not described in knowing the device is using vanilla BACnet4J.
-
OK, maybe this was the motivation to close the add() methods. But I purchased the library to implement it in my own device where I need the proprietary properties. Looks like I have to contact the author if I am even entitled to make a change in the source code.
-
BACnet4J is owned and licensed by Infinite Automation Systems, Inc. You can contact sales@infiniteautomation.com to inquire about purchasing a license to use it privately in a closed-source manner. The license certainly permits subsequent modification for your application. Note also that BACnet4J is released under the GPL: https://github.com/infiniteautomation/BACnet4J/blob/master/license.txt
So if you're comfortable releasing whatever you're creating open source under the GPL you do not need to acquire a license.
-
I purchased the license because I am using it in my closed project. The question is if I am allowed to make changes in the code I purchased. I will clear it with the sales.
-
I confirmed you have a license. Yes you may privately modify BACnet4J for derivative uses. What you purchased was, "BacNet4J Commercial License for unlimited use in Custom Products" where the key word is unlimited. The only thing forbidden then would be selling derivative BACnet4J licenses (the right to sell private licenses is not granted in making custom products) to my knowledge, but sales could clarify that if need be.
-
Thank you for clarification.
-
Hello,
unfortunately, only now I have had time to get involved in this discussion. I have now bypassed my problem by limiting the reading of multiple properties in a request to read the properties known to bacnet4j. The proprietary properties I read now individually with a single read request from the device. This is not nice and for me it means losing time with bacnet4j version 4, but I get the information in several reading cycles, which I read under bacnet4j version 3 in one reading cycle. Since I have seen that bacnet4j version 5 is now shipped, I'll try to find a way to get the information from the device I'm interrogating in one read cycle. The topic propertyArrayIndex from release notes version 5 could perhaps be the cause of the my problem. I will continue to report if my problem with version 5 can be solved.case LIFE_SAFETY_POINT : final Encodable value = new ReadProperty(localDevice, remoteDevice) .readProperty(oId, PropertyIdentifier.forId(MY_SPECIAL_PROBLEM_EVENT_MESSAGE_TEXTS), new UnsignedInteger(3)); logger.debug("Encodable: [{}]", value); txt = ((CharacterString) value).getValue();
-
My Problem is fixed with bacnet4j 5 thank you.
It is possible again to add proprietary values:
final ObjectPropertyTypeDefinition o = new ObjectPropertyTypeDefinition( ObjectType.lifeSafetyZone, true, new PropertyTypeDefinition( PropertyIdentifier.forId(MY_SPECIAL_VALUE), CharacterString.class, true)); ObjectProperties.getObjectPropertyTypeDefinitions(ObjectType.lifeSafetyPoint).add(o); final ObjectPropertyTypeDefinition o2 = new ObjectPropertyTypeDefinition( ObjectType.lifeSafetyPoint, true, new PropertyTypeDefinition( PropertyIdentifier.forId(MY_SPECIAL_VALUE), CharacterString.class, true)); ObjectProperties.getObjectPropertyTypeDefinitions(ObjectType.lifeSafetyPoint).add(o2);
in this example above MY_SPECIAL_VALUE is a list (SequenceOf<CharacterString>)