AX Community Status

Updated a site to 4.15 and customer figured out the SysInfo function is broken because a change Tridium made.

Noticed a post a year ago on the AX Community SourceForge site with no reply:

https://www.niagara-community.com/s/article/Breaking-Change-Constructing-paths-to-subdirectories-of-Niagara-Home

See SysInfo component, lines 280-290
https://sourceforge.net/p/niagaraaxcommun/code/HEAD/tree/N4/axCommunity-rt/src/org/axcommunity/niagara/system/BSysInfo.java#l280

Unclear how many other components may be affected by this. Is anyone still maintaining this project?

Realize supporting software for free is a thank-less job, but is anyone going to support AX Community moving forward?

Thanks

Ken

To my knowledge, it is no longer maintained. What component were you using that was broken?

The SysInfo function is broken. The link to what Tridium changed and examples of replacement code is in my post.

Be nice to find the next generation of Tridium programming gurus to take ownership.

I would take a shot at fixing it, but don’t have a current Java complier setup. Not sure the built-in Tridium complier will work.

Unfortunately, breaking changes happen like this all the time. Admittedly, I’ve never used much AxCommunity because we had many customers (when I worked for several BAS Contractors) who wouldn’t allow modules line AxCommunity or VykonPro in their installations

The problem with open source modules such as this is that they may not have the ability to be maintained over the lifespan of a framework. VykonPro will always be maintained since Vykon is Tridium’s OEM channel

Also, what did you upgrade from? I assume it was an early rev of 4.10? Those changes mentioned started over 3 years ago.

The object in its entirety would have to be put in a module, at a minimum a program module. Since it’s accessing protected spaces, there are safeguards from program objects accessing these.

I’ll have to research this more. I’ll load this on a 9000 that I have with 4.15 and monitor.

So it looks like the only line that needs to change in the code is:

try {
  getNiagaraHome().setValue(SystemFiles.getNiagaraHomeDirectory().toString());
} catch (Exception e) {
  getNiagaraHome().setValue("");
}

It looks like the others would stay as-is since SystemFiles doesn’t expose those paths

You bring up an interesting point about how many people are actually using AXCommunity. I wonder how many installed systems are out there. Did notice a post on this forum asking what functions people use.

It would be interesting to see Tridium adopt the AXCommunity module and make it repository for popular customer requested add-ins. They could having a voting session at the summit.

Like for example, I don’t know what all information people really look at for the SysInfo. I saw where it gives an error to me in my 9000 in that try/catch block… But honestly, I don’t know how many people need information about protected spaces and such. I also don’t like the fact that it allows you to restart or reboot from that block… I think that’s a terrible design because of a customer gets a hold of that and just reboots willy nilly from that object…. Seems kinda bad.

Here’s an example of my program object I have for a station info in a 9000. I also have one for the 8000 and our JENEsys edge controllers as well.

There is some good information from that SysInfo block, but I think the information about the homes needn’t be there. I’d also hide the actions.

You get the picture though.

The TstatB block from AxCommunity is extremely useful as it is a genuine Hysteresis Block that can sense if it’s direct or reverse acting.

Just try using the StringToFile function in 4.15 and I get access errors. Made sure the path and file have write permission and I can edit the file from workbench and notepad.

SEVERE [19:04:24 27-Mar-26 EDT][axCommunity.StringToFile]
slot:/Drivers/Playground/StringToFile
access denied (“java.io.FilePermission” “file:^AQI\Gen_log.csv” “write”)
[Ljava.lang.StackTraceElement;@1843904f
Exception in thread “Thread-26” java.lang.RuntimeException: java.security.AccessControlException: access denied (“java.io.FilePermission” “file:^AQI\Gen_log.csv” “write”)
at org.axcommunity.niagara.system.BStringToFile$FileThread.run(BStringToFile.java:60)
Caused by: java.security.AccessControlException: access denied (“java.io.FilePermission” “file:^AQI\Gen_log.csv” “write”)

Not sure if something in Tridium changed and this is another AXCommunity function that needs repair.

Just looking for a function to append string data to a text file. Really question the value of Tridium if you can’t do simple file IO from the environment.

This likely isn’t a Tridium issue directly. It’s more a side effect of using a module that’s no longer actively maintained.

Niagara 4.15 introduced a number of security changes, especially around file system access, so behavior like this isn’t entirely unexpected. If the module is relying on older or internal APIs, those can change at any time and aren’t guaranteed to remain compatible.

Tridium generally does a good job documenting breaking changes, but third-party modules still need to be updated to stay aligned with those changes. If they aren’t, things that once worked can start failing as the platform evolves.

It’s similar to any community-developed module—if development stops and the platform continues to move forward, compatibility issues are almost inevitable.

So this is less about a limitation of the platform and more about the need for the module to be updated to meet current security and API requirements.

I guess we just put it on the list of AXCommunity modules that need to be updated.

In the meantime I need to write a series of values to a .CSV log file. I have looked around for examples and everything points to using the Java.IO.writer function. Just figured that is what StringtoFile used and don’t feel like going down rabbit holes that don’t work.

Do you know of any file write examples that work in 4.15?

Thanks

I’ll take a look when I get home. Flying back today from teach an N4 class.

Looking back at some notes from the a developer session I see this:

Security manager restricts file operations to:

Station Home

Sys.getStationHome()

Niagara User Home

Sys.getNiagaraSharedUserHome()

Pretty sure I tried those paths, but will look again.

Using the complete Windows path in StringtoFile to the station files directory works:

C:\ProgramData\Niagara4.15\vykon\stations\Test\shared\

Been using Tridium since 2006 and this is the time I have ever used a fully qualified OS path name to a file!

It’s strange that you have to do that.

Here’s why I find that so strange:

public void onExecute() throws Exception {
    // Generate the filename with the current date in the format: "MyCSVFile MM-DD-YY.csv"
    String fileName = generateFileName();

    // Create a BOrd object using the path composed by the getDirectory() method concatenated with the filename
    BOrd fileOrd = createFileOrd(fileName);

    FileWriter fileWriter = null;

    try {
        // Try to get the file from the fileOrd if it exists, or make a new file
        BIFile file = getFile(fileOrd, fileName);

        // Initialize a FileWriter to write to the file
        fileWriter = initializeFileWriter(file);

        // Write a string to the file
        fileWriter.write("This is my file. There are many like this but this one is mine");
        
    } catch (IOException e) {
        // Print an error message and the stack trace if an exception occurs
        System.out.println("Failed creating file " + fileName);
        e.printStackTrace();
    } finally {
        // Ensuring that the FileWriter is properly closed in a finally block after writing to the file
        closeFileWriter(fileWriter);
    }
}

private String generateFileName() {
    // Get the current system time
    BAbsTime time = Clock.time();
    
    // Generate the filename using the current date
    return "MyCSVFile " + time.getMonth().getMonthOfYear() + "-" + time.getDay() + "-" + time.getYear() % 100 + ".csv";
}

private BOrd createFileOrd(String fileName) {
    // Generate the BOrd (Binary-Object Resource Descriptor) for the file from its file path
    return BOrd.make(getDirectory() + "/" + fileName);
}

private BIFile getFile(BOrd fileOrd, String fileName) throws IOException {
    try {
        // Try fetch an existing file
        BIFile file = (BIFile) fileOrd.get(fileOrd.resolve().getComponent());
        System.out.println("A file named " + fileName + " already exists.");
        return file;
    } catch (UnresolvedException ure) {
        // In case the file doesn't exist, create a new file
        FilePath filePath = getLastQuery(fileOrd);
        System.out.println("Creating a new file named " + fileName);
        return BFileSystem.INSTANCE.makeFile(filePath);
    }
}

private FilePath getLastQuery(BOrd fileOrd) {
    // Parse the BOrd object fileOrd to an array ofOrdQuery objects
    OrdQuery[] query = fileOrd.parse();
    
    // Retrieve the last query from the array, and cast it to a FilePath
    return (FilePath) query[query.length - 1];
}

private FileWriter initializeFileWriter(BIFile file) throws IOException {
    // Convert the file to a local file
    File localFile = BFileSystem.INSTANCE.pathToLocalFile(file.getFilePath());
    
    // Create and return a FileWriter object to write to the file
    return new FileWriter(localFile, false);
}

private void closeFileWriter(FileWriter fileWriter) {
    if (fileWriter != null) {
        try {
            // Ensure that the FileWriter is closed to release resources
            fileWriter.close();
        } catch (IOException e) {
            // Print the stack trace for the exception
            e.printStackTrace();
        }
    }
}

That is a program object that requires no security requirements and uses all public API.

2026-04-02_13-23-59

That is the directory in which I’m writing to. This is also on a supervisor running a station.

The entire Java.io file stuff is really messing with me! Inspired by getting StringTo File working, decided to press on with adding writing an event log CSV file only to be stumped again by File.exists which only works with the Unix path delimiter / instead of windows \.

All I wanted to do is check to see if the file was there and if not create a new one with a header before writing the latest event values.

Thank you for listening to my rants, sometimes it feels lonely out here…

FYI, I did look at the CSV writer code you used in your example which only added to my confusion.

On a plane heading home and will pick it up again once my inspiration returns.

I will send you my file exists sample for you to look at in a bit. Maybe you can see the errors of my ways.

Here is my simple test for file. Notice the / delimiter.

public void onExecute() throws Exception
{
// execute code (set executeOnChange flag on inputs)
File file = new File(“C:/ProgramData/Niagara4.15/vykon/stations/Test/shared/AQI/temp.txt”);
boolean cond1 = file.exists();
if(cond1){
setStatus(“true”);
}else{
setStatus(“false”);
};
}

Here is the path I use in StringToFile:

C:\ProgramData\Niagara4.15\vykon\stations\Test\shared\AQI\

Feel like I am struggling to write “hello world”