Category Archives: Tridium

Reading a Rainforest Automation EAGLE with a Jace

I’ve always been concerned about utility use in my house.  With a wife and a couple of young boys, keeping our utility bills under control can be a challenging task.  I’ve put together a few projects in the past to help monitor our consumption as it’s happening, and they’ve generally helped identify energy hogs and bad practices in my house.  The solutions I’ve come up with in the past were fairly rudimentary, and weren’t easy for my family to read.  I wished there was a simpler way to read the consumption information off the shiny new “smart” meter that our friends at our local utility installed.  It collects a bunch of really cool data that they let me access the next day or so on their website, but that doesn’t help me identify problems that are happening right now.  If only there was a way we could pull that information in near real-time, we could do some really neat stuff with that.

Enter the Rainforest Automation EAGLE.  What a cool little device!  It communicates directly with the main electric meter at my house via ZigBee, and pulls off the one thing that was of real interest to me – current demand.  The folks over at Rainforest make several different devices that help accomplish this, but the Eagle was the one that interested me the most.  It’s a stand-alone device, and they have an API that lets me pull basically whatever data they get off of my meter into whatever I want.  I won’t go into the minutia of how to set the device up – the Rainforest documentation is more than adequate for that – but I will tell you the whole process took less than 10 minutes.  If you’re interested in monitoring your demand, and you’re lucky enough to live in one of the areas served by the utilities that support this device, I highly recommend you pick one of these bad-boys up.

Once I had the Eagle up and running, it took a little bit of work to get the data from the device into the Jace I have at my house.  I am working on getting all my utility information collected using the Jace so that I can eventually build a portal for my house that I can use to do all my monitoring and control.  This was the first device that I started reading via the Jace, and it was simple to get up and running.  The Eagle reports using a simple XML structure that can be accessed through a few different methods, all of which are published on the Rainforest Automation site.  It’s easily read using the simple Niagara XML parser.  The great thing about reading this data into the Jace is that we can then perform other calculations and store our data using the capabilities already present in the Niagara platform.  Right now I’m only trending  the current consumption data, as I have been buried in other projects at work and haven’t had any free time to really dig into what I can do with the Eagle.

I’ve done some rough comparisons between what the Eagle is telling me the meter is seeing, and what my e-Monitor is reporting, and the good news is they’re usually reading within a few percent of each other.  Since the e-Monitor averages its readings and the Eagle output is real-time, it’s expected that they’d be slightly different.

If you want to use the code here in your own Jace, you’ll have to create the program object, change the IP it looks at, and make sure you create the necessary slots as well.  Here’s a screenshot of the Slots tab so you can duplicate it.

Slot Tab

The only thing I’ve had a problem with so far is that the query occasionally hangs up – I’m not exactly sure why.  I inserted a check into the program to try to force a re-query, but apparently that’s not bulletproof.  I’ll put a little more effort into it when I get some free time.  For now, here’s the code, feel free to use it and let me know if you see where my hang up is.

Program Code

 

 

Creating .csv Files in Niagara AX

I recently had the need to create .csv files to store array data in the file space of a Jace.  The application required that I create the file if it didn’t exist, and if it did exist, read the data that was there.  I also needed to save the array values to the .csv file on station shutdown, and at other intervals depending on if the Jace was a ‘hard’ Jace or a ‘soft’ Jace.  Because the memory used for the file space has a limited number of cycles, it was suggested that I not write to that space more than once a day if necessary, so that’s why the different flavors of Jaces have different write periods.

After digging around on Niagara Central and sorting through the API it wasn’t too difficult to put together.  Here’s some snippets of the code, and explanations of what each part does.  This should hopefully get you rolling in the right direction if you have need for implementing something like this.

For the first part, I create the array that I’m going to read / write, and then assign a value to an integer to determine what ‘period’ I am in – for this application I am recording 10 values of 15 minute interval data, hence the 96 rows ( 24 * 4 ) and 10 columns. The first step to creating the file is to create a FilePath object, named fp here. From there, I have a couple of other booleans that determine if I’ve completed certain functions in my class.


double[][] results = new double[96][10];

int lastPeriod = determinePeriod();

FilePath fp;

/* Determine if this is installed on a Soft Jace or a Hard Jace.
* We only want to write to the non-temporary file space on a Hard
* Jace once a day. Using these booleans, we will determine what
* type of device the program is installed on. If installed on a
* Soft Jace, the .csv file will be updated every time the array
* is updated, every midnight, and when the station is shut down.
* If installed on a Hard Jace the .csv file will only be updated
* on station shutdown or at midnight if the station is operating.
*/

boolean isSoftJace = checkDeviceType();
boolean fileWritten = false;

The next important part comes in the onStart() method, or started() if you’re building a module. The basics of this part is that I wanted to create a unique name for each input that I was monitoring, and store each inputs .csv file in a base ‘Arrays’ directory under the file space. I also wanted to organize the input .csv files by the associated equipment. To do that, I determined the name of the program, and the name of the parent of the program, and used those to build a string that I used to create the filepath. From there, I assigned a value to the previously created fp object using the factory method for creating FilePath objects, which is simply just giving it a String value that represents the actual file path that you want to use to read / write to.

After I’ve created the FilePath, I call methods that check if the file currently exists, and if it doesn’t, creates it. After that, I read the values from my file into my array. My file creation method simply writes values of zero to all the expected positions if the file doesn’t exist.

  String name = getProgram().getDisplayName(null);

  String parent = getProgram().getParent().getDisplayName(null);

  // File path using station home as root - Defined here

  fp = new FilePath("Arrays/" + parent + "/" + name + ".csv");

  checkFileExists();

  readArray();

The checkFileExists() method is fairly basic. The method returns a boolean value for use by other methods in my program. To check if the file exists, you simply make a BIFile object, and pass the FilePath object to it. If the file exists, the file object will be non-null. So I check for this, if the file object is null, I call the createFile() method and return a ‘false’. If the file exists, I simple return a ‘true’.

private boolean checkFileExists() {

  boolean fileExists = true;

  // Make a BIFile out of the FilePath

  BIFile file = BFileSystem.INSTANCE.findFile(fp);

  // Determine if the file exists, if it does not, create it.

  if ( file == null ) {

    createFile();

    fileExists = false;
  }

  return fileExists;
}

For all the file I/O, I utilize threads to make sure I don’t kill the watchdog timer. From what I’ve gathered, this is a good practice any time you are reading or writing to a disk, as the operation is relatively slow.

/* The createFile() writeArray() and readArray() methods all create threads
 * for accessing the file system. This is done by creating inner classes
 * and utilizing the run() method implemented by the Runnable interface.
 * This prevents any delays while reading or writing to the disk from
 * impacting the main thread and possibly bombing out the watchdog timer.
 */

private void createFile() {

  Thread createArray = new CreateFile();

  createArray.start();
}

private void writeArray() {

  Thread writeArray = new WriteFile();

  writeArray.start();
}

private void readArray() {

  Thread readArray = new ReadFile();

  readArray.start();
}

The createFile() method is pretty straightforward. I use an OutputStream and a PrintWriter to write the file. You can use a StringBuilder object or any number of ways to make your string, and then you just pass it to your PrintWriter to write it to disk. For my application, I looped through the 96 rows, and had an inner loop that wrote 10 zero’s to populate my array. Do as you need to build your file.

class CreateFile extends Thread {

  public void run() {

    try {

      // Make a BIFile out of the FilePath

      BIFile file = BFileSystem.INSTANCE.makeFile(fp);

      // Now we initialize the files contents with 96 periods with
      // 10 data periods each

      OutputStream outputStream = file.getOutputStream();

      PrintWriter out = new PrintWriter(outputStream, false);

      String content = "";

      ** BUILD YOUR STRING HERE THAT YOU WANT TO WRITE.**

      out.println(content);

      out.close();
    }
    catch (IOException ioe) {

    }
  }
}

Reading the file is essentially the same operation, only I’m populating the array values instead of writing from the array. I created an array of String objects and just iterated over them to get the values that I wanted out of them, and then wrote them to my array.

class ReadFile extends Thread {

public void run() {

  // Create a BIFile out of the passed in FilePath

  BIFile file = BFileSystem.INSTANCE.findFile(fp);

  try {

    // Convert the BIFile to an array of Strings per line

    String[] lines = FileUtil.readLines(file);

    // Iterate through the array of Strings

    for ( int i = 0; i < lines.length; i++ ) {

      // Convert the current String to it's component parts by splitting
      // at each comma

      String[] resultsOfCurrentLine = TextUtil.split(lines[i], ',');

      // Iterate through the array of values returned from splitting
      // each line, and convert the values to doubles

      for ( int j = 0; j < resultsOfCurrentLine.length; j++ ) {

        results[i][j] = Double.parseDouble(resultsOfCurrentLine[j]);
      }
    }
  }
  catch (IOException ioe) {

  }
}

Writing the file once it’s created is also fairly straightforward.

class WriteFile extends Thread {

  public void run() {

    boolean fileExists = checkFileExists();

    if ( fileExists ) {

      try {

        BIFile file = BFileSystem.INSTANCE.findFile(fp);

        OutputStream outputStream = file.getOutputStream();

        PrintWriter out = new PrintWriter(outputStream, false);

        // Loop through all the periods

        for ( int i = 0; i < results.length; i++ ) {

          String content = "";

          for ( int j = 0; j < 10; j++ ) {

            content += results[i][j];

            if ( j < 9 ) content += ",";
          }

          out.println(content);
        }

        out.close();
      }
      catch (IOException ioe) {

      }
    }
  }
}

The only other part that’s interesting is how to determine if you’re utilizing a hard Jace or a soft Jace. I haven’t tested this method fully, but the quick test I did seemed to work. The hard jaces use QNX, so it’s fairly easy to figure this one out.

private boolean checkDeviceType() {

  BComponent sys = (BComponent)BOrd.make("service:platform:SystemPlatformService").get();

  sys.lease();

  BString osName = (BString)sys.get("osName");

  boolean isJace = osName.toString().startsWith("qnx");

  return !isJace;
}

There’s quite a bit of helpful information on this on Niagara Central. If you’re looking for more help, search for “Create a BIFile” That’s where I pulled a lot of this stuff from.  It’s located here : <a title=”How to create a BIFile” href=”http://www.niagara-central.com/ord?portal:/dev/wiki/How-To_Create_a_BIFile” target=”_blank”>Create a BIFile</a>

The only other challenging part is figuring out how you are going to structure your file, and how you’re going to parse the data that you have in your array.  But that’s usually just simple loops, so it shouldn’t be too difficult.  If you need any examples, please shoot me an e-mail and I’ll see what I can throw your way.