Chapter 21 - Create an Auto Request and Response For Your Driver

You only need to worry about this chapter if your driver features the notion of overriding data points. Most equipment runs an internal program that updates some or all of the field-device's data values. Some protocols require a point to be placed into a special mode if and when it is controlled by an entity other than the field-device itself. In our hypothetical protocol such a mode takes effect by transmitting the ASCII string "force" in the byte array of the write request. Field-devices that have a notion of overriding points will usually need to be specially informed if and when the particular data value is no longer being controlled by the external entity. The dev driver framework makes this easy for you to implement in your driver.

To do this, you will create a point-auto request and possibly a point-auto response. Please follow these steps if this is necessary for your driver:

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

  1. Make another Java class that extends BDdfWriteRequest. Name it BYourDriverPointAutoRequest. Create this in the package named com.yourCompany.yourDriver.comm.req. Also add an empty slotomatic comment immediately after the opening brace for the class declaration.

    To do this, create a text file named BYourDriverPointAutoRequest.java in the jarFileName/src/com/yourCompany/yourDriver/comm/rsp folder. Inside the text file, start with the following text:

    package com.yourCompany.yourDriver.comm.req;
    
    import com.tridium.ddf.comm.req.*;
    import javax.baja.sys.*;
    
    public class BYourDriverPointAutoRequest
      extends BDdfWriteRequest
    {
      /*-
      class BYourDriverPointAutoRequest
      {
      }
      -*/
    }
    

  2. Override the toByteArray method. Build the byte array following your protocol's message that un-forces the data point. Please review your driver's protocol once again, if necessary, to find such a message. If you cannot find such a message then you can probably skip this chapter!

    Inside the body of the toByteArray method, you will need to construct a Java byte array and return it. The next step will further describe how to do this.

    Assume that any data you need in order to construct the auto (unforce) message itself, ignoring any details about which particular data value to auto (unforce), is a frozen property on the write parameters structure (the same structure that your driver's write request uses to construct the byte array that it returns from its toByteArray method). If you need more information than your write parameters presently provides, then you will need to update your write parameters structure and add frozen properties for the required information.

    In light of this discussion, please code the toByteArray method to return a byte array that matches the description that your protocol document defines for the message that you identify as the un-force or relinquish-auto request. Please follow this example as a guide:

    public byte[] toByteArray()
    {
      // In the dev driver framework, all requests are automatically
      // Assigned a deviceId when they are created. In addition to the
      // DeviceId, write requests are automatically assigned an instance
      // Of a Write Parameters structure when they are created, this point
      // auto request is an instance of BDdfWriteReqest. The dev driver
      // Framework calls the toByteArray method (function) after it
      // Creates the write request, therefore this particular request has
      // already been assigned a device id, and a Write parameters
      // structure. The deviceId will be an instance of BYourDriverDeviceId,
      // the writeParameters structure will be an instance of
      // BYourDriverWriteParameters,
      // That is how dev driver works! This happens automatically.
    
      BYourDriverDeviceId deviceId =
        (BYourDriverDeviceId)getDeviceId();
    
      BYourDriverWriteParams writeParams =
        (BYourDriverWriteParams)getWriteParameters();
    
      final byte SOH = 0x01;
      final byte EOT = 0x04;
      // In this hypothetical example, the protocol document
      // Indicates that all requests start with a hex 01 byte and
      // All requests end with a hex 04 byte.
      // After the hex 01, the protocol expects a number between
      // 0 and 255 to identify the device, followed by the ASCII
      // Characters "Unforce ", followed by one or more of the
      // following:
      //   ao{X}
      //   do{X}
      // Where:
      //   {X} = a sequence of ASCII digits '0'-'9' to identify which
      //         analog or digital output to change.
      //
      // If more than one of these sequences are present then they are
      // delimited by commas.
      // The message ends with a hex 04 terminator byte.
    
      // ByteArrayOuptutStream is a standard Java class in package java.io
      // Let's use it to help us build the byte array that we will return
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      // Writes the hex 01 start character to bos, our Java stream of
      // bytes.
      bos.write(SOH);
    
      // Writes the unit number onto bos, our Java stream of bytes.
      bos.write(deviceId.getUnitNumber());
    
    
      // Writes the ASCII bytes for "unforce" followed by a space to
      // bos, our Java stream of bytes
      bos.write('u'); bos.write('n');
      bos.write('f'); bos.write('o'); bos.write('r'); bos.write('c'); bos.write('e');
      bos.write(' ');
    
    
      // Loops through all control points that are to be auto 'ed (unforced) by this request.
      // The dev driver framework will try to coalesce all control points that need updated
      // Under a device that have equivalent write parameters into a single write
      // Request. You can get the array of these items by calling the method
      // named getWritableSource. It returns an array of IDdfWritable -- these
      // will be the proxy extensions of your driver's control points.
      IDdfWritable pointsToUpdate[] = getWritableSource();
      for (int i=0; i<pointsToUpdate.length;i++)
      { // This is a good thing to check in case dev driver adds support for
        // Auto'ing components that are not proxy extensions
        if (pointsToUpdate[i] instanceof BYourDriverProxyExt)
        { // Casts the IDdfWritable to BYourDriverProxyExt
          BYourDriverProxyExt updateProxy = (BYourDriverProxyExt)pointsToUpdate[i];
          // Gets the read parameters structure, it helped BYourDriverReadRequest
          // know whether to read analog or digital. We can make optimal use of this
          // property by re-using it here also.
          BYourDriverReadParams proxyReadParams =
            (BYourDriverReadParams)updateProxy.getReadParameters();
          // Gets the point id structure for the particular point. We previously
          // defined it to contain an offset. We can make optimal use of this offset
          // by re-using it here also.
          BYourDriverPointId proxyPointId =
            (BYourDriverPointId)updateProxy.getPointId();
          if (proxyReadParams.getTypeString().equalsIgnoreCase("analog"))
          { // If updating an analog output on the field-device
            bos.write('a'); bos.write('o');
          }
          else if (proxyReadParams.getTypeString().equalsIgnoreCase("digital"))
          { // Else, if updating a digital output on the field-device
            bos.write('d'); bos.write('o');
          }
          else // Sanity check
            throw new RuntimeException("Oops! Auto'ing type string '"+
              proxyReadParams.getTypeString()+"' is not supported.");
          // Writes the point index as a string
          bos.write(Integer.toString(proxyId.getOffset()).getBytes());
    
          if(i+1<pointsToUpdate.length) // If not the last point in the loop
            bos.write(',');             // Then this writes a comma
        } // End of if  instanceof etc.
      } // End of for loop
      // Writes the byte that according to our hypothetical protocol,
      // Indicates the end of the message on the field-bus.
      bos.write(EOT);
      // Converts the stream of bytes in our Java stream of bytes into
      // An actual Java byte array and returns it.
      return bos.toByteArray();
    }
    

  3. Override the processReceive method and return a new instance of your auto response class (to be created in a subsequent step). You can re-use your write response here, if you determine that it provides the same exact functionality that you would need for your point-auto response. This will most likely be the case if you left your write response essentially as an empty class that extends BDdfWriteRequest.

    To recap part of day 1's lesson, the dev driver framework calls the toByteArray method (function), transmits the resulting byte array onto the field-bus, looks for incoming data frames, and passes them to this method (until this method returns a response (not null), throws an exception, or times out.

    Please implement the processReceive using the following Java code as a guide. You may notice that this hypothetical example is nearly identical to the processReceive method that was in the example for the read request!

    public BIDdfResponse processReceive(IDdfDataFrame recieveFrame)
       throws DdfResponseException
    { // We coded our hypothetical receiver such that received frames always have
      // at least 2 bytes. So lets not worry about checking for that
      String responseStatus = new String( // Constructs a string from bytes
        receiveFrame.getFrameBytes(),      // In the receive frame buffer
        2,                                // Starting at index 2, taking
        receiveFrame.getFrameSize()-3);      // All chars except SOH, unitNbr, and EOT
      // According to our hypothetical protocol, the device responds 'OK' if the
      // Request succeeds. Any other string denies the write. In the event of a
      // Denial, the string itself describes the denial in plain English.
      if (responseStatus.equalsIgnoreCase("OK"))
        return new BYourDriverWriteResponse(); // <-In this case our auto
                                               //   response has the
                                               //   equivalent functionality
                                               //   as BYourDriverWriteResponse
                                               //   So we can re-use it.
      else
        throw new DdfResponseException("Equipment Nak During Auto - "+responseStatus);
    }
    

  4. Run slotomatic and perform a full build on your driver (as described in Chapter 2).
  5. Create your point-auto response, if you were not able to re-use your write response.

    As a guide, please follow the instructions in the earlier chapter of today's lesson where we showed you how to create your driver's write response.

  6. Associate your auto request to your write parameters class.

    Do this by changing the class declaration on BYourDriverWriteParams to specify that it also implements the BIDdfAutoParams interface. Then define a method called getDevAutoRequestType on BYourDriverWriteRequest

    Follow this example as a guide:

    package com.yourCompany.yourDriver.identify;
    
    import com.tridium.ddf.identify.*;
    import javax.baja.sys.*;
    
    public class BYourDriverWriteParams
      extends BDdfIdParams
      implements BIDdfWriteParams, BIDdfAutoParams
    {
      /*-
      class BYourDriverWriteParams
      {
        properties
        {
          forceWrite : boolean
            -- This property has nothing to with the dev
            -- driver framework itself. Instead, we need
            -- to construct the toByteArray method of the
            -- driver's write request in following the
            -- driver's protocol to write data values.
            -- In this hypothetical protocol, if we do not
            -- forceWrite then the equipment's internal
            -- program could overwrite any change that
            -- Niagara might make to a data value.
            default{[true]}
        }
      }
      -*/
      public Type getWriteRequestType(){return BYourDriverWriteRequest.TYPE;}
      public Type getAutoRequestType(){return BYourDriverPointAutoRequest.TYPE;}
    }
    

  7. Run slotomatic and perform a full build on your driver (as described in Chapter 2).