Ignition Settings Next Page


In the OPC device settings there is a table with different devices, and then once you select that device and click next it pulls up a settings record different from the others.

This looks like a recordActionTable, but how do I get the ‘next’ intermediary step so that I could have a panel for multiple different settings types?

You have to supply one or more DriverType instances in your module hook. The DriverType instances supply the information needed to show up in that list of drivers, and provide all of the information needed for the OPC Server to offer the correct settings record combination, and then to instantiate your driver with such a record.

@pturmel In my application I want to create the settings list itself, but I am not actually creating drivers here. I basically want to recreate how that settings workflow works, but not for drivers.

Example: User selects create new Fleet

a list of fleet types is shown with check boxes (like that above)

User selects a fleet and clicks next,

A specific settings record for that fleet is pulled up.

Ok. Well, the first thing will be supplying an IConfigTab via your GatewayHook’s .getConfigPanels() method. The config tab will show up in the ConfigCategory it names, using the translatable menu entry given. Use DefaultConfigTab.builder() to handle the details.

Your IConfigTab instance must override .getPanel() to actually deliver a configuration page. This first page would be a subclass of ConfigPanel that presents your bullet list. No settings record, just programmatically placed wicket form elements. Your onSubmit() method would examine the radio buttons and select the next ConfigPanel to display.

You’re going to have to get comfortable with wicket. /:

Or have your first ConfigPanel deliver a React app. (:

It’s more specific than your needs, but check out com.inductiveautomation.ignition.gateway.web.components.RemoteConfigItemChoicePanel for a relatively simple starting point.

To use it, have your ExtensionPointType implementation (should probably extend BaseExtensionPointType) return that new panel, then the callback for the panel will actually edit your records. In this case, there’s actually two steps in the “wizard” - first, the remote gateway is selected, then that remote gateway selection brings up the choice of remote providers powered by the RemoteConfigItemChoicePanel.

    public ConfigPanel newRecordConfigPanel(PersistentRecord[] records, IConfigPage configPage,
                                            ConfigPanel parentPanel) {
        super.newRecordConfigPanel(records, configPage, parentPanel);
        return new RemoteGatewayChoicePanel(...) {
            protected void onGatewaySelected(ServerId gateway) {
1 Like

The device page workflow uses com.inductiveautomation.ignition.gateway.web.components.ExtensionPointPage. It takes care of everything you’re seeing, but you do have to start using ExtensionPointType to model your thing and the types of sub-things that exist.


@Kevin.Herron @pturmel @PGriffith Looks like I’ve got some programming to do, as always thank you for the quick responses.

I will see what I can come up with.

Hi Kevin,

Do you have any examples of ExtensionPointType/ExtensionPointPage being used?

Check the Slack example; it extends the Alarm Notification system’s extension point.

Thanks I’ll check it out!

Hey sorry to keep asking so many questions. I keep re reading the javadocs trying to understand how to implement the ExtentionPointPage and ExtensionPointType. I cannot figure it out, and the slack example also didn’t really help ,e understand unfortunately.

I gave a feeble attempt at writing some code for ExtensionPointPage. The javadoc does not document what getExtensionPointManager() does.

Heres my attempt:

And heres my type… which I do not know where/how it connects to anything. Is the type supposed to be something specific? Am I supposed to make a persistentRecord for each ExtensionPointType? I am so lost.

Okay, so your ExtensionPointManager needs to be implemented. That should be the (effectively singleton) ‘manager’ that book-keeps all the individual instances you’re going to create.
That interface has two methods that align pretty well with a backing map -
ExtensionPointType getExtensionPoint(String typeId);
List<? extends ExtensionPointType> getExtensionPoints();
Your ExtensionPointPage implementation will rely on those implementations to drive itself. Your ExtensionPointPage itself should have a type parameter that maps to your ‘base’ record type - whatever common settings there are between your different item types - think things like name, description.
See how it’s implemented on the AlarmJournalPage:

Your base settings record should have (by convention) a StringField named Type:

    public static final StringField Type = new StringField(META, "Type", SFieldFlags.SMANDATORY,

Then, each ‘type’ of sub-configurable-thing you have needs to have its own PersistentRecord implementation, which will have a (by convention) Profile field referencing back to its parent, and a BaseExtensionPoint subclass, which will define a type ID (that ultimately ends up in the base record’s Type field). This is what the Profile field should look like - only with the type parameters for your ‘base’ record class:

    public static final LongField ProfileId = new LongField(META, "ProfileId", SFieldFlags.SPRIMARY_KEY);
    public static final ReferenceField<AlarmJournalRecord> Profile = new ReferenceField<>(META,
        AlarmJournalRecord.META, "Profile", ProfileId

It’s easiest to use the ‘conventional’ names for these things, because it makes everything else automatically work without having to override various other methods.

The ‘type’ that’s passed into BaseExtensionPointType as a constructor parameter is the same type that will be used to look up the extension point type on the ExtensionPointManager - this whole area ties together.

Thanks @PGriffith. So do I have to make my own implementation of ExtensionPointManager if my Settings Panel will not be under opc devices, or alarming etc?

Correct. It should probably be something you manage in your GatewayHook’s setup, startup, shutdown methods.

I have created this.

What is the method on setup/shutdown I call to add/remove this implementation to the gateway context? If such method exists…

The ExtensionPointPage needs to point back to the ExtensionPointManager. You don’t explicitly register the manager anywhere in the gateway hook - it’s just easiest to manage your module’s lifecycle using those methods. You do have to register your ExtensionPointPage, via the config category/config panels on the GatewayModuleHook.

Do I need a field called “Profile” in my base records to complete this linkage? Module wont startup right now.

Are you running SchemaUpdater#updatePersistentRecords(com.inductiveautomation.ignition.gateway.localdb.persistence.RecordMeta<?>...) in your module’s setup hook? You need to run that (every time!) to ensure all the fields your PersistentRecord defines are present in the IDB.

SchemaUpdater is accessible from GatewayContext.

I call this method on setup()


Try clearing out your .idb and starting from scratch - it looks like you’ve got an already present record that’s breaking the contract you established on the PersistentRecord. If everything’s set up correctly with the extension point, then you shouldn’t be able to get a null profile.