• Recent
    • Tags
    • Popular
    • Register
    • Login
    1. Home
    2. robert bouwens
    3. Posts

    Please Note This forum exists for community support for the Mango product family and the Radix IoT Platform. Although Radix IoT employees participate in this forum from time to time, there is no guarantee of a response to anything posted here, nor can Radix IoT, LLC guarantee the accuracy of any information expressed or conveyed. Specific project questions from customers with active support contracts are asked to send requests to support@radixiot.com.

    Radix IoT Website Mango 3 Documentation Website Mango 4 Documentation Website Mango 5 Documentation Website
    R
    • Profile
    • Following 0
    • Followers 0
    • Topics 12
    • Posts 36
    • Best 0
    • Controversial 0
    • Groups 0

    Posts made by robert bouwens

    • RE: Bug in class AcknowledgementService

      the missing class:

      
      package com.serotonin.bacnet4j.service.acknowledgement;
      
      import com.serotonin.bacnet4j.exception.BACnetServiceException;
      import com.serotonin.util.queue.ByteQueue;
      
      public class DeleteObjectAck extends AcknowledgementService {
      	private static final long serialVersionUID = 3837098889443642131L;
      
      	public static final byte TYPE_ID = 11;
      
      	public DeleteObjectAck() {
      	}
      
      	@Override
      	public byte getChoiceId() {
      		return TYPE_ID;
      	}
      
      	@Override
      	public void write(ByteQueue queue) {
      	}
      
      	DeleteObjectAck(ByteQueue queue) throws BACnetServiceException {
      	}
      
      	@Override
      	public String toString() {
      		return "DeleteObjectAck()";
      	}
      
      	@Override
      	public int hashCode() {
      		final int PRIME = 31;
      		int result = 1;
      		result = PRIME * result;
      		return result;
      	}
      
      	@Override
      	public boolean equals(Object obj) {
      		if (this == obj)
      			return true;
      		if (obj == null)
      			return false;
      		if (getClass() != obj.getClass())
      			return false;
      		final DeleteObjectAck other = (DeleteObjectAck) obj;
      		if (this == other)
      			return true;
      		else
      			return false;
      	}
      }
      
      
      

      the result is not used anyway - if it fails the exception and the appr. error will be catched.

      posted in BACnet4J general discussion
      R
      robert bouwens
    • Bug in class AcknowledgementService

      we try to delete objects and found that the appropriate request is not handled at all.

      
      	        if (type == CreateObjectAck.TYPE_ID) // 10
      	            return new CreateObjectAck(queue);
      	        if (type == ReadPropertyAck.TYPE_ID) // 12
      	            return new ReadPropertyAck(queue);
      
      

      the service with type_id equal 11 is not coded!
      the missing code:

      
      	        if (type == DeleteObjectAck.TYPE_ID) // 11
      	            return new DeleteObjectAck(queue);
      
      

      and that new class is also not coded :)
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Bug in class AcknowledgementService

      we try to delete objects and found that the appropriate request is not handled at all.

      
      	        if (type == CreateObjectAck.TYPE_ID) // 10
      	            return new CreateObjectAck(queue);
      	        if (type == ReadPropertyAck.TYPE_ID) // 12
      	            return new ReadPropertyAck(queue);
      
      

      the service with type_id equal 11 is not coded!
      the missing code:

      
      	        if (type == DeleteObjectAck.TYPE_ID) // 11
      	            return new DeleteObjectAck(queue);
      
      

      and that new class is also not coded :)
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • Bug when sending segmented messages
              if (segmentsRequired > request.getMaxSegmentsAccepted() || segmentsRequired > 128)
                  throw new BACnetException("Response too big to send to device; too many segments required:" + segmentsRequired + ", accepted: " + request.getMaxSegmentsAccepted());
      

      above the code to determine the amount of acceptable segment messages.

      the implementation for getMaxSegmentsAccepted is wrong. it is defined as a bitmap.
      so we should change that piece of code to:

          public int getMaxSegmentsAccepted() {
          	switch (maxSegmentsAccepted)
          	{
          	case 0:
          		return 64;
          	case 1:
          		return 2;
          	case 2:
          		return 4;
          	case 3:
          		return 8;
          	case 4:
          		return 16;
          	case 5:
          		return 32;
          	case 6:
          		return 64;
          	case 7:
          		return 128;
          	
          	}
          	// 3 bits - we can't get here
              return 64;
          }
      
      

      this is in the class ConfirmedRequest.

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Bug when sending segmented messages

      if (segmentsRequired > request.getMaxSegmentsAccepted() || segmentsRequired > 128)
      throw new BACnetException("Response too big to send to device; too many segments required:" + segmentsRequired + ", accepted: " + request.getMaxSegmentsAccepted());
      above the code to determine the amount of acceptable segment messages.

      the implementation for getMaxSegmentsAccepted is wrong. it is defined as a bitmap.
      so we should change that piece of code to:

          public int getMaxSegmentsAccepted() {
          	switch (maxSegmentsAccepted)
          	{
          	case 0:
          		return 64;
          	case 1:
          		return 2;
          	case 2:
          		return 4;
          	case 3:
          		return 8;
          	case 4:
          		return 16;
          	case 5:
          		return 32;
          	case 6:
          		return 64;
          	case 7:
          		return 128;
          	
          	}
          	// 3 bits - we can't get here
              return 64;
          }
      
      

      this is in the class ConfirmedRequest.

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • ReadPropertyMultipleRequest

      when reading all properties from an object, then all properties defined will be returned.
      a bit too much.

          /**
           * add only defined/implemented properties.
           * 
           * @param obj
           * @param results
           * @param pid
           * @param pin
           */
      	private void addProperty(BACnetObject obj, List<Result> results,
      			PropertyIdentifier pid, UnsignedInteger pin) {
      		if (pid.intValue() == PropertyIdentifier.all.intValue()) {
                              // the properties implemented
      			List<PropertyIdentifier> properties = obj.getProperties();
      
      			for (PropertyIdentifier pd : properties) {
      				addProperty(obj, results, pd, pin);
      			}
      		} else if (pid.intValue() == PropertyIdentifier.required.intValue()) {
      			for (PropertyTypeDefinition def : ObjectProperties
      					.getRequiredPropertyTypeDefinitions(obj.getId()
      							.getObjectType()))
      				addProperty(obj, results, def.getPropertyIdentifier(), pin);
      		} else if (pid.intValue() == PropertyIdentifier.optional.intValue()) {
      			for (PropertyTypeDefinition def : ObjectProperties
      					.getOptionalPropertyTypeDefinitions(obj.getId()
      							.getObjectType()))
      				addProperty(obj, results, def.getPropertyIdentifier(), pin);
      		} else {
      			// Get the specified property.
      			try {
      				results.add(new Result(pid, pin, obj.getPropertyRequired(pid,
      						pin)));
      			} catch (BACnetServiceException e) {
      				results.add(new Result(pid, pin, new BACnetError(e
      						.getErrorClass(), e.getErrorCode())));
      			}
      		}
      	}
      
      

      in the class BACnetObject we add:

          /**
           * return all implemented properties
           * 
           * @return
           */
          public List<PropertyIdentifier> getProperties()
          {
          	ArrayList<PropertyIdentifier> list = new ArrayList<PropertyIdentifier>();
          	for ( PropertyIdentifier pid : properties.keySet())
          	{
          		list.add(pid);
          	}
          	return list;
          }
      
      

      with this modification only implemented and default properties will be taken into account.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • Statefull handling of the property services supported

      the stack is willing to answer every service request and this is bad ;-)
      at startup we defined the implemented services via the service supported property.
      un- and confirmed requests will be handled by the following corrections:

          /**
           * statefull handling of the property services supported
           * 
           * @param services
           * @param type
           * @param queue
           * @return
           * @throws BACnetException
           */
          public static UnconfirmedRequestService createUnconfirmedRequestService(ServicesSupported services, byte type, ByteQueue queue)
                  throws BACnetException {
              if (type == IAmRequest.TYPE_ID)
              {
              	if (services.isIAm())
              		return new IAmRequest(queue);
              	else
              		return null;
              }
              if (type == IHaveRequest.TYPE_ID)
              {
              	if (services.isIHave())
              		return new IHaveRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedCovNotificationRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedCovNotification())
              		return new UnconfirmedCovNotificationRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedEventNotificationRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedEventNotification())
              		return new UnconfirmedEventNotificationRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedPrivateTransferRequest.TYPE_ID)
              		if (services.isUnconfirmedPrivateTransfer())
              			return new UnconfirmedPrivateTransferRequest(queue);
              	else
              		return null;
              if (type == UnconfirmedTextMessageRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedTextMessage())
              		return new UnconfirmedTextMessageRequest(queue);
              	else
              		return null;
              }
              if (type == TimeSynchronizationRequest.TYPE_ID)
              {
              	if ( services.isTimeSynchronization())
              		return new TimeSynchronizationRequest(queue);
              	else
              		return null;
              }
              if (type == WhoHasRequest.TYPE_ID)
              {
              	if (services.isWhoHas())
                  return new WhoHasRequest(queue);
              	else
              		return null;
              }
              if (type == WhoIsRequest.TYPE_ID)
              {
              	if (services.isWhoIs())
              		return new WhoIsRequest(queue);
              	else
              		return null;
              }
              if (type == UTCTimeSynchronizationRequest.TYPE_ID)
              {
              	if (services.isUtcTimeSynchronization())
              		return new UTCTimeSynchronizationRequest(queue);
              	else
              		return null;
              }
      
              throw new BACnetException("Unsupported unconfirmed service: " + (type & 0xff));
          }
      
      

      we also need to mod the the class UnconfirmedRequest:

          public UnconfirmedRequest(ServicesSupported services, ByteQueue queue) throws BACnetException {
              queue.pop();
              byte choiceId = queue.pop();
              service = UnconfirmedRequestService.createUnconfirmedRequestService(services, choiceId, queue);
          }
      
      

      when the apdu will be created we need to take the upper mods into account and:

      abstract public class APDU {
          public static APDU createAPDU(ServicesSupported services, ByteQueue queue) throws BACnetException {
              // Get the first byte. The 4 high-order bits will tell us the type of PDU this is.
              byte type = queue.peek(0);
              type = (byte) ((type & 0xff) >> 4);
      
              if (type == ConfirmedRequest.TYPE_ID)
                  return new ConfirmedRequest(services, queue);
              if (type == UnconfirmedRequest.TYPE_ID)
                  return new UnconfirmedRequest(services, queue);
      
      

      unconfirmed request will not be answered when the service supported flag set to false.
      for confirmed service request it's a bit tricky.

          public static ConfirmedRequestService createConfirmedRequestService(ServicesSupported services, byte type, ByteQueue queue)
                  throws BACnetException {
              if (type == AcknowledgeAlarmRequest.TYPE_ID) // 0
              {
              	if (services.isAcknowledgeAlarm())
              		return new AcknowledgeAlarmRequest(queue);
              }
              else if (type == ConfirmedCovNotificationRequest.TYPE_ID) // 1
              {
              	if (services.isConfirmedCovNotification())
              		return new ConfirmedCovNotificationRequest(queue);
              }
              else if (type == ConfirmedEventNotificationRequest.TYPE_ID) // 2
              {
              	if (services.isConfirmedEventNotification())
              		return new ConfirmedEventNotificationRequest(queue);
              }
              else if (type == GetAlarmSummaryRequest.TYPE_ID) // 3
              {
              	if (services.isGetAlarmSummary())
              		return new GetAlarmSummaryRequest(queue);
              }
              else if (type == GetEnrollmentSummaryRequest.TYPE_ID) // 4
              {
              	if (services.isGetEnrollmentSummary())
              		return new GetEnrollmentSummaryRequest(queue);
              	else
              		throw new BACnetRejectException(RejectReason.unrecognizedService);
              }
              else if (type == SubscribeCOVRequest.TYPE_ID) // 5
              {
              	if (services.isConfirmedCovNotification())
              		return new SubscribeCOVRequest(queue);
              }
              else if (type == AtomicReadFileRequest.TYPE_ID) // 6
              {
              	if (services.isAtomicReadFile())
              		return new AtomicReadFileRequest(queue);
              }
              else if (type == AtomicWriteFileRequest.TYPE_ID) // 7
              {
              	if (services.isAtomicWriteFile())
              		return new AtomicWriteFileRequest(queue);
              }
              else if (type == AddListElementRequest.TYPE_ID) // 8
              {
              	if (services.isAddListElement())
              		return new AddListElementRequest(queue);
              } 
              else if (type == RemoveListElementRequest.TYPE_ID) // 9
              {
              	if (services.isRemoveListElement())
              		return new RemoveListElementRequest(queue);
              }
              else if (type == CreateObjectRequest.TYPE_ID) // 10
              {
              	if (services.isCreateObject())
              		return new CreateObjectRequest(queue);
              }
              else if (type == DeleteObjectRequest.TYPE_ID) // 11
              {
              	if (services.isDeleteObject())
              		return new DeleteObjectRequest(queue);
              }
              else if (type == ReadPropertyRequest.TYPE_ID) // 12
              {
                  return new ReadPropertyRequest(queue);
              }
              else if (type == ReadPropertyConditionalRequest.TYPE_ID) // 13
              {
              	if (services.isReadPropertyConditional())
              		return new ReadPropertyConditionalRequest(queue);
              }
              else if (type == ReadPropertyMultipleRequest.TYPE_ID) // 14
              {
              	if (services.isReadPropertyMultiple())
              		return new ReadPropertyMultipleRequest(queue);
              }
              else if (type == WritePropertyRequest.TYPE_ID) // 15
              {
              	if (services.isWritePropertyMultiple())
              		return new WritePropertyRequest(queue);
              }
              else if (type == WritePropertyMultipleRequest.TYPE_ID) // 16
              {
              	if (services.isWritePropertyMultiple())
              		return new WritePropertyMultipleRequest(queue);
              }
              else if (type == DeviceCommunicationControlRequest.TYPE_ID) // 17
              {
                 	if (services.isDeviceCommunicationControl())
                 		return new DeviceCommunicationControlRequest(queue);
              }
              else if (type == ConfirmedPrivateTransferRequest.TYPE_ID) // 18
              {
              	if (services.isConfirmedPrivateTransfer())
              		return new ConfirmedPrivateTransferRequest(queue);
              }
              else if (type == ConfirmedTextMessageRequest.TYPE_ID) // 19
              {
              	if (services.isConfirmedTextMessage())
              		return new ConfirmedTextMessageRequest(queue);
              }
              else if (type == ReinitializeDeviceRequest.TYPE_ID) // 20
              {
                 	if (services.isReinitializeDevice())
                 		return new ReinitializeDeviceRequest(queue);
              }
              else if (type == VtOpenRequest.TYPE_ID) // 21
              {
              	if (services.isVtOpen())
              		return new VtOpenRequest(queue);
              }
              else if (type == VtCloseRequest.TYPE_ID) // 22
              {
              	if (services.isVtClose())
              		return new VtCloseRequest(queue);
              }
              else if (type == VtDataRequest.TYPE_ID) // 23
              {
                 	if (services.isVtData())
                 		return new VtDataRequest(queue);
              }
              else if (type == AuthenticateRequest.TYPE_ID) // 24
              {
                 	if (services.isAuthenticate())
                 		return new AuthenticateRequest(queue);
              }
              else if (type == RequestKeyRequest.TYPE_ID) // 25
              {
              	if (services.isRequestKey())
              		return new RequestKeyRequest(queue);
              }
              else if (type == ReadRangeRequest.TYPE_ID) // 26
              {
              	if (services.isReadRange())
              		return new ReadRangeRequest(queue);
              }
              else if (type == LifeSafetyOperationRequest.TYPE_ID) // 27
              {
              	if (services.isLifeSafetyOperation())
                  return new LifeSafetyOperationRequest(queue);
              }
              else if (type == SubscribeCOVPropertyRequest.TYPE_ID) // 28
              {
              	if (services.isSubscribeCovProperty())
              		return new SubscribeCOVPropertyRequest(queue);
              }
              else if (type == GetEventInformation.TYPE_ID) // 29
              {
              	if (services.isGetEventInformation())
              		return new GetEventInformation(queue);
             }
      
              throw new BACnetErrorException(ErrorClass.device, ErrorCode.serviceRequestDenied);
          }
      
      

      the mods for the class, added membervariable services:

      public class ConfirmedRequest extends APDU implements Segmentable {
          public static final byte TYPE_ID = 0;
          private ServicesSupported services;
      ...
      

      in the class ipmessagecontrol we make shure it matches:

                  // Create the APDU.
                  try {
                  	ServicesSupported ss = (ServicesSupported) localDevice.getConfiguration().getProperty(PropertyIdentifier.protocolServicesSupported);
                      return APDU.createAPDU(ss, queue);
                  }
      
      

      and now i am open for discussions :)

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: ReadPropertyMultipleRequest

      when reading all properties from an object, then all properties defined will be returned.
      a bit too much.

          /**
           * add only defined/implemented properties.
           * 
           * @param obj
           * @param results
           * @param pid
           * @param pin
           */
      	private void addProperty(BACnetObject obj, List<Result> results,
      			PropertyIdentifier pid, UnsignedInteger pin) {
      		if (pid.intValue() == PropertyIdentifier.all.intValue()) {
                              // the properties implemented
      			List<PropertyIdentifier> properties = obj.getProperties();
      
      			for (PropertyIdentifier pd : properties) {
      				addProperty(obj, results, pd, pin);
      			}
      		} else if (pid.intValue() == PropertyIdentifier.required.intValue()) {
      			for (PropertyTypeDefinition def : ObjectProperties
      					.getRequiredPropertyTypeDefinitions(obj.getId()
      							.getObjectType()))
      				addProperty(obj, results, def.getPropertyIdentifier(), pin);
      		} else if (pid.intValue() == PropertyIdentifier.optional.intValue()) {
      			for (PropertyTypeDefinition def : ObjectProperties
      					.getOptionalPropertyTypeDefinitions(obj.getId()
      							.getObjectType()))
      				addProperty(obj, results, def.getPropertyIdentifier(), pin);
      		} else {
      			// Get the specified property.
      			try {
      				results.add(new Result(pid, pin, obj.getPropertyRequired(pid,
      						pin)));
      			} catch (BACnetServiceException e) {
      				results.add(new Result(pid, pin, new BACnetError(e
      						.getErrorClass(), e.getErrorCode())));
      			}
      		}
      	}
      
      

      in the class BACnetObject we add:

          /**
           * return all implemented properties
           * 
           * @return
           */
          public List<PropertyIdentifier> getProperties()
          {
          	ArrayList<PropertyIdentifier> list = new ArrayList<PropertyIdentifier>();
          	for ( PropertyIdentifier pid : properties.keySet())
          	{
          		list.add(pid);
          	}
          	return list;
          }
      
      

      with this modification only implemented and default properties will be taken into account.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Statefull handling of the property services supported

      the stack is willing to answer every service request and this is bad ;-)
      at startup we defined the implemented services via the service supported property.
      un- and confirmed requests will be handled by the following corrections:

          /**
           * statefull handling of the property services supported
           * 
           * @param services
           * @param type
           * @param queue
           * @return
           * @throws BACnetException
           */
          public static UnconfirmedRequestService createUnconfirmedRequestService(ServicesSupported services, byte type, ByteQueue queue)
                  throws BACnetException {
              if (type == IAmRequest.TYPE_ID)
              {
              	if (services.isIAm())
              		return new IAmRequest(queue);
              	else
              		return null;
              }
              if (type == IHaveRequest.TYPE_ID)
              {
              	if (services.isIHave())
              		return new IHaveRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedCovNotificationRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedCovNotification())
              		return new UnconfirmedCovNotificationRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedEventNotificationRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedEventNotification())
              		return new UnconfirmedEventNotificationRequest(queue);
              	else
              		return null;
              }
              if (type == UnconfirmedPrivateTransferRequest.TYPE_ID)
              		if (services.isUnconfirmedPrivateTransfer())
              			return new UnconfirmedPrivateTransferRequest(queue);
              	else
              		return null;
              if (type == UnconfirmedTextMessageRequest.TYPE_ID)
              {
              	if (services.isUnconfirmedTextMessage())
              		return new UnconfirmedTextMessageRequest(queue);
              	else
              		return null;
              }
              if (type == TimeSynchronizationRequest.TYPE_ID)
              {
              	if ( services.isTimeSynchronization())
              		return new TimeSynchronizationRequest(queue);
              	else
              		return null;
              }
              if (type == WhoHasRequest.TYPE_ID)
              {
              	if (services.isWhoHas())
                  return new WhoHasRequest(queue);
              	else
              		return null;
              }
              if (type == WhoIsRequest.TYPE_ID)
              {
              	if (services.isWhoIs())
              		return new WhoIsRequest(queue);
              	else
              		return null;
              }
              if (type == UTCTimeSynchronizationRequest.TYPE_ID)
              {
              	if (services.isUtcTimeSynchronization())
              		return new UTCTimeSynchronizationRequest(queue);
              	else
              		return null;
              }
      
              throw new BACnetException("Unsupported unconfirmed service: " + (type & 0xff));
          }
      
      

      we also need to mod the the class UnconfirmedRequest:

          public UnconfirmedRequest(ServicesSupported services, ByteQueue queue) throws BACnetException {
              queue.pop();
              byte choiceId = queue.pop();
              service = UnconfirmedRequestService.createUnconfirmedRequestService(services, choiceId, queue);
          }
      
      

      when the apdu will be created we need to take the upper mods into account and:

      abstract public class APDU {
          public static APDU createAPDU(ServicesSupported services, ByteQueue queue) throws BACnetException {
              // Get the first byte. The 4 high-order bits will tell us the type of PDU this is.
              byte type = queue.peek(0);
              type = (byte) ((type & 0xff) >> 4);
      
              if (type == ConfirmedRequest.TYPE_ID)
                  return new ConfirmedRequest(services, queue);
              if (type == UnconfirmedRequest.TYPE_ID)
                  return new UnconfirmedRequest(services, queue);
      
      

      unconfirmed request will not be answered when the service supported flag set to false.
      for confirmed service request it's a bit tricky.

          public static ConfirmedRequestService createConfirmedRequestService(ServicesSupported services, byte type, ByteQueue queue)
                  throws BACnetException {
              if (type == AcknowledgeAlarmRequest.TYPE_ID) // 0
              {
              	if (services.isAcknowledgeAlarm())
              		return new AcknowledgeAlarmRequest(queue);
              }
              else if (type == ConfirmedCovNotificationRequest.TYPE_ID) // 1
              {
              	if (services.isConfirmedCovNotification())
              		return new ConfirmedCovNotificationRequest(queue);
              }
              else if (type == ConfirmedEventNotificationRequest.TYPE_ID) // 2
              {
              	if (services.isConfirmedEventNotification())
              		return new ConfirmedEventNotificationRequest(queue);
              }
              else if (type == GetAlarmSummaryRequest.TYPE_ID) // 3
              {
              	if (services.isGetAlarmSummary())
              		return new GetAlarmSummaryRequest(queue);
              }
              else if (type == GetEnrollmentSummaryRequest.TYPE_ID) // 4
              {
              	if (services.isGetEnrollmentSummary())
              		return new GetEnrollmentSummaryRequest(queue);
              	else
              		throw new BACnetRejectException(RejectReason.unrecognizedService);
              }
              else if (type == SubscribeCOVRequest.TYPE_ID) // 5
              {
              	if (services.isConfirmedCovNotification())
              		return new SubscribeCOVRequest(queue);
              }
              else if (type == AtomicReadFileRequest.TYPE_ID) // 6
              {
              	if (services.isAtomicReadFile())
              		return new AtomicReadFileRequest(queue);
              }
              else if (type == AtomicWriteFileRequest.TYPE_ID) // 7
              {
              	if (services.isAtomicWriteFile())
              		return new AtomicWriteFileRequest(queue);
              }
              else if (type == AddListElementRequest.TYPE_ID) // 8
              {
              	if (services.isAddListElement())
              		return new AddListElementRequest(queue);
              } 
              else if (type == RemoveListElementRequest.TYPE_ID) // 9
              {
              	if (services.isRemoveListElement())
              		return new RemoveListElementRequest(queue);
              }
              else if (type == CreateObjectRequest.TYPE_ID) // 10
              {
              	if (services.isCreateObject())
              		return new CreateObjectRequest(queue);
              }
              else if (type == DeleteObjectRequest.TYPE_ID) // 11
              {
              	if (services.isDeleteObject())
              		return new DeleteObjectRequest(queue);
              }
              else if (type == ReadPropertyRequest.TYPE_ID) // 12
              {
                  return new ReadPropertyRequest(queue);
              }
              else if (type == ReadPropertyConditionalRequest.TYPE_ID) // 13
              {
              	if (services.isReadPropertyConditional())
              		return new ReadPropertyConditionalRequest(queue);
              }
              else if (type == ReadPropertyMultipleRequest.TYPE_ID) // 14
              {
              	if (services.isReadPropertyMultiple())
              		return new ReadPropertyMultipleRequest(queue);
              }
              else if (type == WritePropertyRequest.TYPE_ID) // 15
              {
              	if (services.isWritePropertyMultiple())
              		return new WritePropertyRequest(queue);
              }
              else if (type == WritePropertyMultipleRequest.TYPE_ID) // 16
              {
              	if (services.isWritePropertyMultiple())
              		return new WritePropertyMultipleRequest(queue);
              }
              else if (type == DeviceCommunicationControlRequest.TYPE_ID) // 17
              {
                 	if (services.isDeviceCommunicationControl())
                 		return new DeviceCommunicationControlRequest(queue);
              }
              else if (type == ConfirmedPrivateTransferRequest.TYPE_ID) // 18
              {
              	if (services.isConfirmedPrivateTransfer())
              		return new ConfirmedPrivateTransferRequest(queue);
              }
              else if (type == ConfirmedTextMessageRequest.TYPE_ID) // 19
              {
              	if (services.isConfirmedTextMessage())
              		return new ConfirmedTextMessageRequest(queue);
              }
              else if (type == ReinitializeDeviceRequest.TYPE_ID) // 20
              {
                 	if (services.isReinitializeDevice())
                 		return new ReinitializeDeviceRequest(queue);
              }
              else if (type == VtOpenRequest.TYPE_ID) // 21
              {
              	if (services.isVtOpen())
              		return new VtOpenRequest(queue);
              }
              else if (type == VtCloseRequest.TYPE_ID) // 22
              {
              	if (services.isVtClose())
              		return new VtCloseRequest(queue);
              }
              else if (type == VtDataRequest.TYPE_ID) // 23
              {
                 	if (services.isVtData())
                 		return new VtDataRequest(queue);
              }
              else if (type == AuthenticateRequest.TYPE_ID) // 24
              {
                 	if (services.isAuthenticate())
                 		return new AuthenticateRequest(queue);
              }
              else if (type == RequestKeyRequest.TYPE_ID) // 25
              {
              	if (services.isRequestKey())
              		return new RequestKeyRequest(queue);
              }
              else if (type == ReadRangeRequest.TYPE_ID) // 26
              {
              	if (services.isReadRange())
              		return new ReadRangeRequest(queue);
              }
              else if (type == LifeSafetyOperationRequest.TYPE_ID) // 27
              {
              	if (services.isLifeSafetyOperation())
                  return new LifeSafetyOperationRequest(queue);
              }
              else if (type == SubscribeCOVPropertyRequest.TYPE_ID) // 28
              {
              	if (services.isSubscribeCovProperty())
              		return new SubscribeCOVPropertyRequest(queue);
              }
              else if (type == GetEventInformation.TYPE_ID) // 29
              {
              	if (services.isGetEventInformation())
              		return new GetEventInformation(queue);
             }
      
              throw new BACnetErrorException(ErrorClass.device, ErrorCode.serviceRequestDenied);
          }
      
      

      the mods for the class, added membervariable services:

      public class ConfirmedRequest extends APDU implements Segmentable {
          public static final byte TYPE_ID = 0;
          private ServicesSupported services;
      ...
      

      in the class ipmessagecontrol we make shure it matches:

                  // Create the APDU.
                  try {
                  	ServicesSupported ss = (ServicesSupported) localDevice.getConfiguration().getProperty(PropertyIdentifier.protocolServicesSupported);
                      return APDU.createAPDU(ss, queue);
                  }
      
      

      and now i am open for discussions :)

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: A small bug in the atomicwritefilerequest

      hello matt,

      a zero length file should be readable without error.
      and the standard says explicitly when the file size exceeds.

      if (start < 0 || start > file.length()

      is correct when reading the spec.
      and another commercial stack shows the same behaviour with the upper mod.

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: A small bug in the atomicwritefilerequest

      hi matt,
      sorry for being unclear:

      public class AtomicReadFileRequest extends ConfirmedRequestService
      {
      	private static final long serialVersionUID = -279843621668191530L;
      
      	public static final byte TYPE_ID = 6;
      
      	private final ObjectIdentifier fileIdentifier;
      	private final boolean recordAccess;
      	private final SignedInteger fileStartPosition;
      	private final UnsignedInteger requestedCount;
      
      	public AtomicReadFileRequest(ObjectIdentifier fileIdentifier, boolean recordAccess, SignedInteger fileStartPosition,
      			UnsignedInteger requestedCount)
      	{
      		this.fileIdentifier = fileIdentifier;
      		this.recordAccess = recordAccess;
      		this.fileStartPosition = fileStartPosition;
      		this.requestedCount = requestedCount;
      	}
      
      

      the varaible is the standard member variable defined in the class.

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • A small bug in the atomicwritefilerequest

      basically it works.
      but the standard says exactly when a bacneterrorexception has to be thrown:

      • negative start
      • start exceeds the filesize

      at the same time a few internal 'things' should be checked. when the device property backupAndRestoreState
      is set to preparingForBackup or preparingForRestore the the atomic read/write requests are dangerous and could deliver
      faulty results. thus throw a device configuration in progress bacnetserviceexception as written in the standard.

      
      	@Override
      	public AcknowledgementService handle(LocalDevice localDevice, Address from, Network network) throws BACnetException
      	{
      		AtomicReadFileAck response;
      
      		BACnetObject obj;
      		FileObject file;
      		try
      		{
      			// Find the file.
      			obj = localDevice.getObjectRequired(fileIdentifier);
      			if (!(obj instanceof FileObject))
      			{
      				System.out.println("File access request on an object that is not a file");
      				throw new BACnetServiceException(ErrorClass.object, ErrorCode.rejectInconsistentParameters);
      			}
      			// check for status (backup/restore)
      			BackupState bsOld = (BackupState) localDevice.getConfiguration().getProperty(
      					PropertyIdentifier.backupAndRestoreState);
      			if (bsOld.intValue() == BackupState.preparingForBackup.intValue()
      					|| bsOld.intValue() == BackupState.preparingForRestore.intValue())
      			{
      				// send error: device configuration in progress as response
      				throw new BACnetServiceException(ErrorClass.device, ErrorCode.configurationInProgress);
      			}
      
      			file = (FileObject) obj;
      
      			// Validation.
      			FileAccessMethod fileAccessMethod = (FileAccessMethod) file.getProperty(PropertyIdentifier.fileAccessMethod);
      			if (recordAccess && fileAccessMethod.equals(FileAccessMethod.streamAccess) || !recordAccess
      					&& fileAccessMethod.equals(FileAccessMethod.recordAccess))
      				throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.invalidFileAccessMethod);
      		}
      		catch (BACnetServiceException e)
      		{
      			throw new BACnetErrorException(getChoiceId(), e);
      		}
      
      		if (recordAccess)
      			throw new NotImplementedException();
      
      		long start = fileStartPosition.longValue();
      		long length = requestedCount.longValue();
      
      		/* 
      		 * throw an exception when the following conditions are met:
      		 * - start is a negative number
      		 * - start exceeds the length of the file object
      		 */
      		if (start > file.length() || start < 0)
      			throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.invalidFileStartPosition);
      
      		try
      		{
      			response = new AtomicReadFileAck(new Boolean(file.length() <= start + length), fileStartPosition, file.readData(
      					start, length));
      		}
      		catch (IOException e)
      		{
      			throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.fileAccessDenied);
      		}
      
      		return response;
      	}
      
      

      that's it - works nicely.
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: A small bug in the atomicwritefilerequest

      basically it works.
      but the standard says exactly when a bacneterrorexception has to be thrown:

      • negative start
      • start exceeds the filesize

      at the same time a few internal 'things' should be checked. when the device property backupAndRestoreState
      is set to preparingForBackup or preparingForRestore the the atomic read/write requests are dangerous and could deliver
      faulty results. thus throw a device configuration in progress bacnetserviceexception as written in the standard.

      
      	@Override
      	public AcknowledgementService handle(LocalDevice localDevice, Address from, Network network) throws BACnetException
      	{
      		AtomicReadFileAck response;
      
      		BACnetObject obj;
      		FileObject file;
      		try
      		{
      			// Find the file.
      			obj = localDevice.getObjectRequired(fileIdentifier);
      			if (!(obj instanceof FileObject))
      			{
      				System.out.println("File access request on an object that is not a file");
      				throw new BACnetServiceException(ErrorClass.object, ErrorCode.rejectInconsistentParameters);
      			}
      			// check for status (backup/restore)
      			BackupState bsOld = (BackupState) localDevice.getConfiguration().getProperty(
      					PropertyIdentifier.backupAndRestoreState);
      			if (bsOld.intValue() == BackupState.preparingForBackup.intValue()
      					|| bsOld.intValue() == BackupState.preparingForRestore.intValue())
      			{
      				// send error: device configuration in progress as response
      				throw new BACnetServiceException(ErrorClass.device, ErrorCode.configurationInProgress);
      			}
      
      			file = (FileObject) obj;
      
      			// Validation.
      			FileAccessMethod fileAccessMethod = (FileAccessMethod) file.getProperty(PropertyIdentifier.fileAccessMethod);
      			if (recordAccess && fileAccessMethod.equals(FileAccessMethod.streamAccess) || !recordAccess
      					&& fileAccessMethod.equals(FileAccessMethod.recordAccess))
      				throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.invalidFileAccessMethod);
      		}
      		catch (BACnetServiceException e)
      		{
      			throw new BACnetErrorException(getChoiceId(), e);
      		}
      
      		if (recordAccess)
      			throw new NotImplementedException();
      
      		long start = fileStartPosition.longValue();
      		long length = requestedCount.longValue();
      
      		/* 
      		 * throw an exception when the following conditions are met:
      		 * - start is a negative number
      		 * - start exceeds the length of the file object
      		 */
      		if (start > file.length() || start < 0)
      			throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.invalidFileStartPosition);
      
      		try
      		{
      			response = new AtomicReadFileAck(new Boolean(file.length() <= start + length), fileStartPosition, file.readData(
      					start, length));
      		}
      		catch (IOException e)
      		{
      			throw new BACnetErrorException(getChoiceId(), ErrorClass.object, ErrorCode.fileAccessDenied);
      		}
      
      		return response;
      	}
      
      

      that's it - works nicely.
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • Correct destination network behaviour

      a device should only respond to messages (who is service) where the destination bacnet network matches.
      currently the stack does not know anything about bacnet networks.
      adding the needed information is simple:

      
         public LocalDevice(int deviceId, String broadcastAddress, String localBindAddress, int localBACnetwork) {
              messageControl = new IpMessageControl(localBACnetwork);
      ...
      /code] 
      
      and the class IpMessageControl needs a new member variable:
      [code]
          private static int localBACnetwork;
      
          /**
           * constructor with configured bacnet network number
           * 
           * @param localBACnetwork
           */
          public IpMessageControl(int localBACnetwork)
          {
          	IpMessageControl.localBACnetwork = localBACnetwork;
          }
      
      
      

      the check is simple, in the function parsAPDU the destination network has to be verified.
      after:

      
                  // Network layer protocol control information. See 6.2.2
                  NPCI npci = new NPCI(queue);
                  if (npci.getVersion() != 1)
                      throw new MessageValidationAssertionException("Invalid protocol version: " + npci.getVersion());
                  if (npci.isNetworkMessage())
                      return null; // throw new MessageValidationAssertionException("Network messages are not supported");
      
      

      add the following check:

      
                  // detect the 'correct' bacnet work and do not respond to foreign networks requests
                  if (npci.hasDestinationInfo())
                  {
                      int bacNet = npci.getDestinationNetwork();
                      if (localBACnetwork > 0 && localBACnetwork != bacNet && bacNet != 65535 && bacNet > 0 )
                      {
                      	return null;
                      }
                  }
      
      

      now you are able to pass a few more btl tests.
      and the upper code has been tested.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Correct destination network behaviour

      a device should only respond to messages (who is service) where the destination bacnet network matches.
      currently the stack does not know anything about bacnet networks.
      adding the needed information is simple:

      
         public LocalDevice(int deviceId, String broadcastAddress, String localBindAddress, int localBACnetwork) {
              messageControl = new IpMessageControl(localBACnetwork);
      ...
      /code] 
      
      and the class IpMessageControl needs a new member variable:
      [code]
          private static int localBACnetwork;
      
          /**
           * constructor with configured bacnet network number
           * 
           * @param localBACnetwork
           */
          public IpMessageControl(int localBACnetwork)
          {
          	IpMessageControl.localBACnetwork = localBACnetwork;
          }
      
      
      

      the check is simple, in the function parsAPDU the destination network has to be verified.
      after:

      
                  // Network layer protocol control information. See 6.2.2
                  NPCI npci = new NPCI(queue);
                  if (npci.getVersion() != 1)
                      throw new MessageValidationAssertionException("Invalid protocol version: " + npci.getVersion());
                  if (npci.isNetworkMessage())
                      return null; // throw new MessageValidationAssertionException("Network messages are not supported");
      
      

      add the following check:

      
                  // detect the 'correct' bacnet work and do not respond to foreign networks requests
                  if (npci.hasDestinationInfo())
                  {
                      int bacNet = npci.getDestinationNetwork();
                      if (localBACnetwork > 0 && localBACnetwork != bacNet && bacNet != 65535 && bacNet > 0 )
                      {
                      	return null;
                      }
                  }
      
      

      now you are able to pass a few more btl tests.
      and the upper code has been tested.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • Small bug in the Datetime class

      public long getTimeMillis()

      constructs a gregoriancalender and takes the bacnetyear as one parameter.
      this is wrong, for the year the memberfunction getCenturyYear() of the date class has to be used.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Small bug in the Datetime class

      public long getTimeMillis()

      constructs a gregoriancalender and takes the bacnetyear as one parameter.
      this is wrong, for the year the memberfunction getCenturyYear() of the date class has to be used.

      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Date has a smal bug

      @joolz said:

      What a coincidence, ...
      hi,

      thanks for testing :-)

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens
    • RE: Date has a smal bug

      also

      
      
          @Override
          public void writeImpl(ByteQueue queue) {
          	int shortYear = year;
          	if (shortYear > 1900)
          	{
          		shortYear -= 1900;
          	}
      
      
      posted in BACnet4J general discussion
      R
      robert bouwens
    • Date has a smal bug
      
          //
          // Reading and writing
          //
          public Date(ByteQueue queue) {
              readTag(queue);
              year = queue.popU1B() + 1900;
              month = Month.valueOf(queue.pop());
              day = queue.popU1B();
              dayOfWeek = DayOfWeek.valueOf(queue.pop());
          }
      
      

      the fix offset of 1900 is wrong.
      it should be:

      
              int tmpyear = queue.popU1B();
              if (tmpyear != 255)
              {
              	tmpyear += 1900;
              }
              year = tmpyear;
      
      

      else the function isYearUnspecified will never return true.
      causes a glitch when displaying an unspecified year.

      regards
      robert

      posted in BACnet4J general discussion
      R
      robert bouwens