diff --git a/src/main/java/no/entra/bacnet/internal/services/ServiceParser.java b/src/main/java/no/entra/bacnet/internal/services/ServiceParser.java index 356aa7b..f34bb65 100644 --- a/src/main/java/no/entra/bacnet/internal/services/ServiceParser.java +++ b/src/main/java/no/entra/bacnet/internal/services/ServiceParser.java @@ -8,6 +8,7 @@ import no.entra.bacnet.objects.ObjectId; import no.entra.bacnet.properties.ReadPropertyMultipleResponse; import no.entra.bacnet.properties.ReadPropertyMultipleService; +import no.entra.bacnet.property.ReadPropertyResponse; import no.entra.bacnet.property.ReadPropertyService; import no.entra.bacnet.services.*; import org.slf4j.Logger; @@ -84,8 +85,19 @@ protected static ParserResult parseServiceExpectingReply(ConfirmedServi switch (serviceChoice) { case ReadProperty: - ParserResult readSinglePropertyResult = ReadSinglePropertyResultParser.parse(hexString); - ReadPropertyService readPropertyService = new ReadPropertyService(); + ParserResult readSingleParserResult = ReadSinglePropertyResultParser.parse(hexString); + if (parserResult.isParsedOk()) { + ReadPropertyService readPropertyService = new ReadPropertyService(); + ReadSinglePropertyResult readSinglePropertyResult = readSingleParserResult.getParsedObject(); + ReadPropertyResponse response = new ReadPropertyResponse(readSinglePropertyResult); + readPropertyService.setReadPropertyResponse(response); + parserResult.setParsedObject(readPropertyService); + parserResult.setParsedOk(true); + } else { + log.trace("Failed to parse ReadProperty. Result is {}. ServiceChoice: {}", readSingleParserResult, serviceChoice); + parserResult.setErrorMessage("Failed to parse ReadProperty. Result is: " + readSingleParserResult + ", ServiceChoice: " + serviceChoice); + } + break; case ReadPropertyMultiple: ParserResult readPropertyMultipleResult = ReadObjectPropertiesResultParser.parse(hexString); @@ -107,7 +119,7 @@ protected static ParserResult parseServiceExpectingReply(ConfirmedServi parserResult.setParsedObject(readPropertyMultipleService); parserResult.setParsedOk(true); } else { - log.trace("Failed to parse ReadProperty or ReadPropertyMultiple. Result is {}. ServiceChoice: {}", readPropertyMultipleResult, serviceChoice); + log.trace("Failed to parse ReadPropertyMultiple. Result is {}. ServiceChoice: {}", readPropertyMultipleResult, serviceChoice); } break; default: diff --git a/src/main/java/no/entra/bacnet/properties/ReadPropertyMultipleService.java b/src/main/java/no/entra/bacnet/properties/ReadPropertyMultipleService.java index 207c1d3..0ede53b 100644 --- a/src/main/java/no/entra/bacnet/properties/ReadPropertyMultipleService.java +++ b/src/main/java/no/entra/bacnet/properties/ReadPropertyMultipleService.java @@ -137,7 +137,7 @@ public String buildHexString() { @Override public boolean expectReply() { - return false; + return true; } public static ReadPropertyMultipleService parse(String hexString) { diff --git a/src/main/java/no/entra/bacnet/property/ReadPropertyResponse.java b/src/main/java/no/entra/bacnet/property/ReadPropertyResponse.java new file mode 100644 index 0000000..ff0ee03 --- /dev/null +++ b/src/main/java/no/entra/bacnet/property/ReadPropertyResponse.java @@ -0,0 +1,50 @@ +package no.entra.bacnet.property; + +import no.entra.bacnet.internal.property.ReadSinglePropertyResult; +import org.slf4j.Logger; + +import java.util.Objects; + +import static org.slf4j.LoggerFactory.getLogger; + +/* +{ + "objectId": "device, 8", + "property-identifier": "PropertiesServicesSupported", + "property-array-index": 0, + "value": "00000111000000000000101111000000000000001111100000000000" +} + */ +public class ReadPropertyResponse { + private static final Logger log = getLogger(ReadPropertyResponse.class); + + private final ReadSinglePropertyResult result; + + public ReadPropertyResponse(ReadSinglePropertyResult result) { + this.result = result; + } + + public ReadSinglePropertyResult getResult() { + return result; + } + + @Override + public String toString() { + return "ReadPropertyResponse{" + + "result=" + result + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ReadPropertyResponse that = (ReadPropertyResponse) o; + return Objects.equals(getResult(), that.getResult()); + } + + @Override + public int hashCode() { + return Objects.hash(getResult()); + } +} diff --git a/src/main/java/no/entra/bacnet/property/ReadPropertyService.java b/src/main/java/no/entra/bacnet/property/ReadPropertyService.java index e32b5ec..4ad89d1 100644 --- a/src/main/java/no/entra/bacnet/property/ReadPropertyService.java +++ b/src/main/java/no/entra/bacnet/property/ReadPropertyService.java @@ -1,16 +1,117 @@ package no.entra.bacnet.property; import no.entra.bacnet.BacnetRequest; +import no.entra.bacnet.apdu.Apdu; +import no.entra.bacnet.bvlc.Bvlc; +import no.entra.bacnet.internal.apdu.MessageType; +import no.entra.bacnet.internal.apdu.SDContextTag; +import no.entra.bacnet.internal.bvlc.BvlcBuilder; +import no.entra.bacnet.internal.bvlc.BvlcFunction; +import no.entra.bacnet.internal.npdu.NpduBuilder; +import no.entra.bacnet.internal.objects.ObjectProperties; +import no.entra.bacnet.internal.properties.PropertyReference; +import no.entra.bacnet.npdu.Npdu; +import no.entra.bacnet.objects.ObjectId; +import no.entra.bacnet.services.ConfirmedServiceChoice; import no.entra.bacnet.services.Service; +import java.util.Set; + +import static no.entra.bacnet.internal.apdu.ArrayTag.ARRAY1_END; +import static no.entra.bacnet.internal.apdu.ArrayTag.ARRAY1_START; +import static no.entra.bacnet.utils.HexUtils.intToHexString; + public class ReadPropertyService extends BacnetRequest implements Service { + private Integer invokeId = null; + private Set objectProperties = null; + private ObjectId objectId = null; + private Set propertyReferences = null; + private ReadPropertyResponse readPropertyResponse = null; + + public ReadPropertyService() { + } + + public Integer getInvokeId() { + return invokeId; + } + + public void setInvokeId(Integer invokeId) { + this.invokeId = invokeId; + } + + public Set getObjectProperties() { + return objectProperties; + } + + public void setObjectProperties(Set objectProperties) { + this.objectProperties = objectProperties; + } + + public ObjectId getObjectId() { + return objectId; + } + + public void setObjectId(ObjectId objectId) { + this.objectId = objectId; + } + + public Set getPropertyReferences() { + return propertyReferences; + } + + public void setPropertyReferences(Set propertyReferences) { + this.propertyReferences = propertyReferences; + } + + public ReadPropertyResponse getReadPropertyResponse() { + return readPropertyResponse; + } + + public void setReadPropertyResponse(ReadPropertyResponse readPropertyResponse) { + this.readPropertyResponse = readPropertyResponse; + } + @Override public String buildHexString() { - return null; + Apdu apdu = Apdu.ApduBuilder.builder() + .withApduType(MessageType.ConfirmedRequest) + .isSegmented(false) + .hasMoreSegments(false) + .isSegmentedReplyAllowed(true) + .withMaxSegmentsAcceptedAbove64() + .withMaxApduLength1476() + .build(); + String apduHexString = apdu.toHexString() + + intToHexString(getInvokeId(), 2) + + ConfirmedServiceChoice.ReadPropertyMultiple.getServiceChoiceHex(); + if (objectId != null && propertyReferences != null) { + apduHexString += SDContextTag.TAG0LENGTH4 + objectId.toHexString(); + apduHexString += ARRAY1_START.toString(); + for (PropertyReference propertyReference : propertyReferences) { + apduHexString += propertyReference.buildHexString(); + } + apduHexString += ARRAY1_END; + } + int numberOfOctets = (apduHexString.length() / 2) + 6; + Bvlc bvlc = new BvlcBuilder(BvlcFunction.OriginalUnicastNpdu).withTotalNumberOfOctets(numberOfOctets).build(); + Npdu npdu = new NpduBuilder().withExpectingReply().build(); + String hexString = bvlc.toHexString() + npdu.toHexString() + apduHexString; + throw new RuntimeException("Not Implemented"); } @Override public boolean expectReply() { - return false; + return true; + } + + @Override + public String toString() { + return "ReadPropertyService{" + + "invokeId=" + invokeId + + ", objectProperties=" + objectProperties + + ", objectId=" + objectId + + ", propertyReferences=" + propertyReferences + + ", readPropertyResponse=" + readPropertyResponse + + "} " + super.toString(); } } diff --git a/src/test/java/no/entra/bacnet/BacnetMessageParserTest.java b/src/test/java/no/entra/bacnet/BacnetMessageParserTest.java index a7a920e..2e1f056 100644 --- a/src/test/java/no/entra/bacnet/BacnetMessageParserTest.java +++ b/src/test/java/no/entra/bacnet/BacnetMessageParserTest.java @@ -1,13 +1,17 @@ package no.entra.bacnet; +import no.entra.bacnet.device.DeviceId; import no.entra.bacnet.error.ErrorClassType; import no.entra.bacnet.error.ErrorCodeType; import no.entra.bacnet.internal.properties.PropertyIdentifier; import no.entra.bacnet.internal.properties.ReadObjectPropertiesResult; import no.entra.bacnet.internal.properties.ReadPropertyResult; +import no.entra.bacnet.internal.property.ReadSinglePropertyResult; import no.entra.bacnet.objects.ObjectId; import no.entra.bacnet.objects.ObjectType; import no.entra.bacnet.properties.ReadPropertyMultipleService; +import no.entra.bacnet.property.ReadPropertyResponse; +import no.entra.bacnet.property.ReadPropertyService; import no.entra.bacnet.services.AbortService; import no.entra.bacnet.services.Service; import no.entra.bacnet.services.WhoIsService; @@ -43,7 +47,7 @@ void parsePropertiesFoundResponse() { Map errorMap = new HashMap(); - errorMap.put(ERROR_CLASS,ErrorClassType.property); + errorMap.put(ERROR_CLASS, ErrorClassType.property); errorMap.put(ERROR_CODE, ErrorCodeType.UnknownProperty); assertEquals(PropertyIdentifier.Units, resultList.get(2).getPropertyIdentifier()); assertEquals(null, resultList.get(2).getReadResult().get(PropertyIdentifier.Units)); @@ -81,7 +85,7 @@ void whoIsWithLowHighRange() { Service service = bacnetResponse.getService(); assertNotNull(service); assertTrue(service instanceof WhoIsService); - WhoIsService whoIsService = (WhoIsService)service; + WhoIsService whoIsService = (WhoIsService) service; assertEquals(1966, whoIsService.getLowRangeLimit()); assertEquals(1966, whoIsService.getHighRangeLimit()); } @@ -92,27 +96,19 @@ void bugfix() { BacnetResponse bacnetResponse = BacnetMessageParser.parse(hexString); assertNotNull(bacnetResponse); } - //FIXME + @Test void readPropertyServicesSupported() { String hexString = "810a001b010030050c0c0200000819613e850707000bc000f8003f"; BacnetResponse bacnetResponse = BacnetMessageParser.parse(hexString); - //protocol-services-supported: (Bit String) (FFFFFFFFFFFFTFTTTTFFFFFFFFFFFFFFTTTTTFFFF) -// assertEquals(deviceId, bacnetResponse.getService().getObjectId()); -// assertEquals(ReadPropertyService.class, bacnetResponse.getService().getClass()); -// assertEquals(PropertyIdentifier.ProtocolServicesSupported, null); - /* - assertTrue(readProperty); - assertFalse(readPropertyConditional); - assertTrue(readPropertyMultiple); - assertTrue(writeProperty); - assertTrue(writePropertyMultiple); - assertTrue(timeSyncronization); - assertTrue(whoHas); - assertTrue(whoIs); - assertTrue(readRange); - assertNotNull(bacnetResponse); + Service service = bacnetResponse.getService(); + assertNotNull(service); + ReadPropertyService propertyService = (ReadPropertyService) service; + ReadPropertyResponse readPropertyResponse = propertyService.getReadPropertyResponse(); + ReadSinglePropertyResult result = readPropertyResponse.getResult(); + assertEquals(PropertyIdentifier.ProtocolServicesSupported, result.getPropertyIdentifier()); + assertEquals(new DeviceId(8), result.getObjectId() ); + assertEquals("00000111000000000000101111000000000000001111100000000000", result.getValue()); - */ } } \ No newline at end of file