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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post Navigation