Chapter 13 - Tell Your Read Response How to Read the Value For Your Driver's Proxy Ext

In the previous chapters of today's lesson, we showed you how to create your driver's read response class. We also helped you start creating your driver's proxy extension class. Please recall from the chapter where you created the read response that we asked you to make the parseReadValue method (function) return null. Please also recall that in the previous chapter we mentioned that the IDdfReadable that is passed to the parseReadValue method (function) of your read response will be an instance of your driver's proxy extension. With all of this being explained, it is now time to finish writing the parseReadValue method of your driver's read response.

While following the examples in this chapter, please replace the text jarFileName, yourDriver and yourCompany as previously described in the Preparation section):

    Please Modify BYourDriverReadResponse.parseReadValue As Follows:
  1. Verify that the given IDdfReadable is an instance of BYourDriverProxyExt. If not then simply return null.
  2. Cast the given IDdfReadable to BYourDriverProxyExt.
  3. Get a reference to the pointId from the given proxy extension and cast it to BYourDriverPointId.

    Do not worry that you have not yet created the BYourDriverPointId class. We will show you how to create it in the next chapter.

  4. Plan that any information you would need to determine the current value of the given proxy extension is available from your driver's point id (such as an offset into the response frame's byte array or some information that will allow you to determine said offset).

    Just as you created your driver's read parameters structure for the purpose of helping you to create the toByteArray method in your read request, you will likewise create your driver's point id for the purpose of helping you to retrieve a particular control point's value from the corresponding read response.

  5. Perform whatever computation and lookup is necessary, from the byte array copy that you made in the constructor of this class, in order to retrieve the correct, current value for the control point that the given proxy extension belongs to. Use the information that you will encode in your point id to determine this. Please use this hypothetical example as a guide:

    // The following imports are for our particular implementation
    // Of the parseReadValue(IDdfReadable) method. Different
    // Drivers might need to import these or other classes to
    // Custom-define the parseReadValue(IDdfReadable) method.
    import java.util.*;
    
    import javax.baja.control.*;
    

      // We will use this array to optimize our implementation of the
      // parseReadValue method.
      private String[] rawValues = null;
      public BStatusValue parseReadValue(IDdfReadable readableSource)
      { // Verify that the given readableSource is an instance of your
        // driver's proxy extension
        if (readableSource instanceof BYourDriverProxyExt)
        { // Casts the given readableSource into a BYourDriverProxyExt
          BYourDriverProxyExt proxy
            = (BYourDriverProxyExt)readableSource;
          // Gets the point id of the given proxy
          BYourDriverPointId pointId
            = (BYourDriverPointId)proxy.getPointId();
          // In our hypothetical protocol, the read request asks to read
          // either analog or digital outputs or inputs. The field device
          // Has up to 30 analog or digital outputs or inputs. The read
          // Response returns all of the corresponding values that were
          // Requested inside a comma delimited string. The substrings in
          // between the commas are signed integers for analog values or
          // The text "on" or "off" for digital values.
          if (rawValues==null) // Parses each of the values from the
            parseRawValues();  // receive frame into a string array.
          // Calls the getReadValue method that takes an int as a
          // Param. We created this method for our own convenience. The
          // getReadValue is private and not part of the developer driver
          // framework. It exists only to help us keep this method short.
          return getReadValue(proxy,pointId);
        }
        else
        {
          return null;
        }
      }
    

      /**
       * This method is called by the parseReadValue method. It is not a
       * requirement for the dev driver framework. Rather, we decided
       * that we needed this method in order to support our own logic
       * in the parseReadValue method.
       */
      private void parseRawValues()
      {
        // The response frame contains a hex 02 character, followed by
        // The device's unit number as one byte, followed by an ASCII
        // Comma delimited string, followed by a hex 04 character.
    
        // Makes a string from the bytes that were in the received frame
        // Starting at location 2 (the third byte since Java arrays
        // Start at index 0). The new string will be parsed starting at
        // Location 2 of the receiveBytes and including as many bytes as
        // The length of the receiveBytes array  minus 3 bytes -- those
        // Three bytes being skipped are the hex 02 char, the unit nbr,
        // And the hex 04 terminating char.
        String commaDelimitedString
          = new String(receiveBytes, 2, receiveBytes.length-3);
    
        // Prepares to count the number of substrings in commaDelimitedString
        int numValues = 0;
        // StringTokenizer is a standard Java utility class that makes it
        // Loops through a string by tokens. Our string tokenizer
        // Will split up the commaDelimitedString based on comma chars.
        StringTokenizer t = new StringTokenizer(commaDelimitedString,",");
        // Loops through the comma delimited string and counts the number
        // Of substrings that are in between commas
        while (t.hasMoreTokens())
        {
          numValues = numValues + 1;
          // Consumes the token to allow us to loop to the next one
          t.nextToken();
        }
        // Allocates an array to hold each individual raw value
        rawValues = new String[numValues];
        // Uses the following int to index into rawValues in the next loop
        int offset = 0;
        // Loops again through all of the substrings that are between commas
        // In the comma delimited string
        t = new StringTokenizer(commaDelimitedString,",");
        while (t.hasMoreTokens())
        {
          rawValues[offset]=t.nextToken();
          offset = offset+1;
        }
        // NOTE: Now we can retrieve the string for any particular value by
        // Directly indexing into the rawValues array.
      }
    
    

      /**
       * We call this method ourselves from the parseReadValue method.
       * It is not required by the developer driver framework, instead, we
       * decided to create this method in order to shorten the
       * parseReadValue method.
       *
       * This method looks up the string value for the given offset and
       * returns it as a BStatusValue that is appropriate for the given
       * proxy extension's driver control point.
       *
       * @param a reference to the BYourDriverProxyExt to parse the read
       * value for.
       *
       * @param a reference to the BYourDriverPointId to use to tell us
       * how to parse the read value from the response bytes.
       *
       * @return a BStatusNumeric, BStatusBoolean, BStatusEnum, or
       * BStatusString that appropriately matches the proxy's control
       * point type. If the proxy's control point is a BNumericWritable
       * or BNumericPoint then this will return a BStatusNumeric that
       * represents the present value of the point. If the proxy's
       * control point is a BBooleanWritable or a BBooleanPoint then
       * this returns a BStatusBoolean that represents the current value
       * of the point. If the proxy's control point is a BEnumWritable
       * or BEnumPoint then this returns BStatusEnum that represents the
       * current value of the point. If this proxy's control point is a
       * BStringPoint or BStringWritable then this returns a BStatusString
       * that represents the current value of the point. Sorry for the
       * verbose description.
       */
      private BStatusValue getReadValue(BYourDriverProxyExt proxy,
                                        BYourDriverPointId  pointId)
      {
        // Gets the raw value at the index for the given proxy
        // NOTE: When we create our pointId, we will give it an int property named offset
        String sRawValue = rawValues[pointId.getOffset()];
        // Normalizes the string raw value into an int
        int iRawValue = 0;
        if (sRawValue.equalsIgnoreCase("on"))
          iRawValue = 1;
        else if (sRawValue.equalsIgnoreCase("off"))
          iRawValue = 0;
        else
          iRawValue = Integer.parseInt(sRawValue);
    
        // Wraps iRawValue into a BStatusValue of the appropriate type
        // For the control point
        BControlPoint controlPoint = proxy.getParentPoint();
        // Checks if the control point is a Numeric Writable or Numeric
        // Point component.
        if (controlPoint instanceof BINumeric)
        { // This is all we need to do! Any scales, offsets, or other
          // Conversions are handled by other parts of Niagara. We just
          // Need to return a raw representation here.
          return new BStatusNumeric(iRawValue);
        }
        // Checks if the control point is a Boolean Writable or a Boolean
        // Point component. NOTE: We must check boolean before enum because
        // a BIBoolean is also a BIEnum!
        else if (controlPoint instanceof BIBoolean)
        { // This is all we need to do. Any polarity conversion is
          // specified and handled elsewhere in Niagara AX
          return new BStatusBoolean(iRawValue>0);
        }
        // Checks if the control point is an Enum Writable or an Enum Point
        // component.
        else if (controlPoint instanceof BIEnum)
        { // A slight extra step is imperative for Enum Points to preserve
          // any dynamic enum range (text to ordinal mapping).
          BStatusEnum e = ((BEnumPoint)controlPoint).getOut();
          return new BStatusEnum( BDynamicEnum.make(
            (int)Math.round(iRawValue),
            e.getValue().getRange()));
        }
        else if (controlPoint instanceof BStringPoint)
        {
          return new BStatusString(sRawValue);
        }
        else
        {
          throw new IllegalStateException(
            "Unsupported control point type: "+
            controlPoint.getType()+"! Please have my program fixed.");
        }
    
      }
    
    

  6. Proceed to the next chapter without performing a build on the response. The response will not successfully compile until you define your driver's point id (BYourDriverPointId). The next chapter explains how to do this.