Exception reading the properties of an AccessDoor Bacnet Object
-
Hi all,
I read succesfully bacnet objects from different type, except one type : "Access Door".
Here the code :
localDevice.getExtendedDeviceInformation(M2MHubDevice); List<ObjectIdentifier> oids = ((SequenceOf<ObjectIdentifier>)localDevice.sendReadPropertyAllowNull(M2MHubDevice, M2MHubDevice.getObjectIdentifier(), PropertyIdentifier.objectList)).getValues(); PropertyReferences refs = new PropertyReferences(); for (ObjectIdentifier oid : oids) { refs.add(oid, PropertyIdentifier.all); } System.out.println("Start read properties"); List<PropertyReferences> refsSmall = refs.getPropertiesPartitioned(1); System.out.println("number of property reference set =" + refsSmall.size() ); PropertyValues pvs = localDevice.readProperties(M2MHubDevice, refsSmall.get(0) ); // now list devices in the object ObjectIdentifier lastOid = null; for( PropertyReferences ref: refsSmall ){ pvs = localDevice.readProperties(M2MHubDevice, ref ); for (ObjectPropertyReference opr : pvs) { if( lastOid == null || !lastOid.equals( opr.getObjectIdentifier() ) ) System.out.println( String.format("\t%s", opr.getObjectIdentifier()) ); lastOid = opr.getObjectIdentifier(); System.out.println(String.format("\t\t%s = %s", opr.getPropertyIdentifier().toString(), pvs.getNoErrorCheck(opr))); } System.out.println("=============="); }
The exception is throwed from the Bacnet4J Encodable.java file - the concerned method is :
protected static void popEnd(ByteQueue queue, int contextId) throws BACnetErrorException { if (readEnd(queue) != contextId) { throw new BACnetErrorException(ErrorClass.property, ErrorCode.missingRequiredParameter); } queue.pop(); if (contextId > 14) queue.pop(); }
When the exception appears, the data are following :
queue=[29,e5,4e,7,8,4f,29,1c,4e,75,c,0,4f,54,45,53,20,44,4f,4f,52,20,31,4f,29,e2,4e,91,0,4f,29,e4,4e,c4,1,0,20,0,4f,29,e8,4e,0,4f,29,ea,5e,91,2,91,20,5f,1f] - readEnd(queue)=-1 - contextId=4
Thanks in advance for your help :)
-
Can you provide either the full stack trace or the complete APDU bytes? There isn't enough context to go on (i.e. what object is currently being deserialized when the exception occurs). The APDU bytes would be preferred.
-
Not sure it helps... :
Below the stack trace of the exception :
Exception in thread "main" com.serotonin.bacnet4j.exception.BACnetErrorException: Property: Missing required parameter at com.serotonin.bacnet4j.type.Encodable.popEnd(Encodable.java:157) at com.serotonin.bacnet4j.type.Encodable.readWrapped(Encodable.java:388) at com.serotonin.bacnet4j.type.Encodable.readEncodable(Encodable.java:349) at com.serotonin.bacnet4j.type.constructed.ReadAccessResult$Result.<init>(ReadAccessResult.java:126) at sun.reflect.GeneratedConstructorAccessor4.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at com.serotonin.bacnet4j.type.Encodable.read(Encodable.java:191) at com.serotonin.bacnet4j.type.constructed.SequenceOf.<init>(SequenceOf.java:66) at com.serotonin.bacnet4j.type.Encodable.readSequenceOf(Encodable.java:262) at com.serotonin.bacnet4j.type.Encodable.readOptionalSequenceOf(Encodable.java:296) at com.serotonin.bacnet4j.type.constructed.ReadAccessResult.<init>(ReadAccessResult.java:64) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) at java.lang.reflect.Constructor.newInstance(Constructor.java:513) at com.serotonin.bacnet4j.type.Encodable.read(Encodable.java:191) at com.serotonin.bacnet4j.type.constructed.SequenceOf.<init>(SequenceOf.java:54) at com.serotonin.bacnet4j.type.Encodable.readSequenceOf(Encodable.java:251) at com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyMultipleAck.<init>(ReadPropertyMultipleAck.java:50) at com.serotonin.bacnet4j.service.acknowledgement.AcknowledgementService.createAcknowledgementService(AcknowledgementService.java:48) at com.serotonin.bacnet4j.apdu.ComplexACK.parseServiceData(ComplexACK.java:189) at com.serotonin.bacnet4j.npdu.ip.IpMessageControl.waitForAck(IpMessageControl.java:696) at com.serotonin.bacnet4j.npdu.ip.IpMessageControl.send(IpMessageControl.java:292) at com.serotonin.bacnet4j.npdu.ip.IpMessageControl.send(IpMessageControl.java:236) at com.serotonin.bacnet4j.LocalDevice.send(LocalDevice.java:430) at com.serotonin.bacnet4j.LocalDevice.send(LocalDevice.java:420) at com.serotonin.bacnet4j.LocalDevice.send(LocalDevice.java:413) at com.serotonin.bacnet4j.LocalDevice.readProperties(LocalDevice.java:869) at com.serotonin.bacnet4j.test.TestARD.main(TestARD.java:139)
And here the serviceRequest object who causes this exception :
If it is not enough, can you say me what function/parameters to survey in the source code? Thank you :)
-
Yeah, i still can't see what it was trying to decode. If you could set a breakpoint at IpMessageControl line 553:
APDU parseApdu() throws Exception { // Initial parsing of IP message. // BACnet/IP if (queue.pop() != (byte) 0x81) <<<<==== HERE throw new MessageValidationAssertionException("Protocol id is not BACnet/IP (0x81)"); byte function = queue.pop();
... and then provide the full contents of "queue", that would be very helpful.
-
Or if it's easier, put a breakpoint at ComplexACK line 189,
public void parseServiceData() throws BACnetException { if (serviceData != null) { ===>>> HERE service = AcknowledgementService.createAcknowledgementService(serviceChoice, serviceData); serviceData = null; } }
A post the contents of "serviceData".
-
Hi Matthew :)
During the last call to the parseServiceDate() function (before the exception appears), serviceData has this value :
[c,7,80,30,0,1e,29,4b,4e,c4,7,80,30,0,4f,29,4d,4e,75,c,0,4f,54,45,53,20,44,4f,
4f,52,20,31,4f,29,4f,4e,91,1e,4f,29,55,4e,91,0,4f,29,6f,4e,82,4,0,4f,29,24,4e,
91,0,4f,29,67,4e,91,0,4f,29,51,4e,10,4f,29,57,4e,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,4f,29,68,4e,91,0,4f,29,e7,4e,91,0,4f,29,e9,4e,91,0,4f,29,eb,4e,91,2,4f,29,
e6,4e,1e,4f,29,e3,4e,64,4f,29,e5,4e,7,8,4f,29,1c,4e,75,c,0,4f,54,45,53,20,44,
4f,4f,52,20,31,4f,29,e2,4e,91,0,4f,29,e4,4e,c4,1,0,20,0,4f,29,e8,4e,0,4f,29,ea,
5e,91,2,91,20,5f,1f] -
Hi jeremie,
The data for the Door Pulse Time is not encoded properly. According to the specification an Unsigned Integer must begin with a tag that provides the length of the value (20.2.4). This is the offending part (in bold):
[c,7,80,30,0,1e,29,4b,4e,c4,7,80,30,0,4f,29,4d,4e,75,c,0,4f,54,45,53,20,44,4f,
4f,52,20,31,4f,29,4f,4e,91,1e,4f,29,55,4e,91,0,4f,29,6f,4e,82,4,0,4f,29,24,4e,
91,0,4f,29,67,4e,91,0,4f,29,51,4e,10,4f,29,57,4e,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,4f,29,68,4e,91,0,4f,29,e7,4e,91,0,4f,29,e9,4e,91,0,4f,29,eb,4e,91,2,4f,29,
e6,4e,1e,4f,29,e3,4e,64,4f,29,e5,4e,7,8,4f,29,1c,4e,75,c,0,4f,54,45,53,20,44,
4f,4f,52,20,31,4f,29,e2,4e,91,0,4f,29,e4,4e,c4,1,0,20,0,4f,29,e8,4e,0,4f,29,ea,
5e,91,2,91,20,5f,1f]The correct encoding of value of 30 is "4e,21,1e,4f".
In "21", the "2" signifies that the type is an UnsignedInteger, and the "1" provides the length (1 byte in this case). I realize that this information can be implied (the type by the property identifier, and the length by whatever is contained in the tags 4e and 4f) but i can't find anywhere in the spec where this alternative encoding is described.
What kind of equipment are you using?
-
Many thanks for your help Matthew!
For your information, I have got an OpenSource BACNET Server for Linux (C/C++), embedded in a proprietary equipment.
-
Ok. Maybe you could inform the developer of the server about this issue, and find out whether this alternate encoding is official, common, or just a mistake.
-
I will :)
In BACNet, reliability property for AccessDoor Object is always required.
Therefore, I think the declaration is uncorrect in the ObjectProperties.java file (com.serotonin.bacnet4j.obj package) :add(ObjectType.accessDoor, PropertyIdentifier.reliability, Reliability.class, false, **false**, null);
=>
add(ObjectType.accessDoor, PropertyIdentifier.reliability, Reliability.class, false, **true**, null);
-
Right you are. The change has been added to the code.