# IdbMigrationStrategy for Device ExtensionPoints

**URL:** <https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028>\
**Category:** Ignition 8.3 Early Access\
**Created:** [November 23, 2024, 12:16am UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028 "2024-11-23T00:16:57Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [November 23, 2024, 12:16am UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/1 "2024-11-23T00:16:57Z")

</div>

I pushed a branch to my [Modbus server driver](https://github.com/kevinherron/modbus-server-driver) that has WIP migration for 8.3.

One of the things it provides an example of is migration from settings to config: [modbus-server-driver/msd-gateway/src/main/java/com/kevinherron/ignition/modbus/ModbusServerModuleHook.java at a00b3e4cfc85775f853156db76987ac0e862beeb · kevinherron/modbus-server-driver · GitHub](https://github.com/kevinherron/modbus-server-driver/blob/a00b3e4cfc85775f853156db76987ac0e862beeb/msd-gateway/src/main/java/com/kevinherron/ignition/modbus/ModbusServerModuleHook.java#L40-L73)

@pturmel you may find this useful if you haven't already worked this out.

TODO: config validation and default values

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [November 23, 2024, 12:19am UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/2 "2024-11-23T00:19:34Z")

</div>

Any reason I can't like this topic?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [November 23, 2024, 12:21am UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/3 "2024-11-23T00:21:16Z")

</div>

I don't know, maybe something about how every topic in this category seems to have a vote associated with it. Not sure who set this up.

edit: just removed the vote thing 🤷

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 19, 2025, 10:16pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/4 "2025-02-19T22:16:08Z")

</div>

BTW, this ended up being **very** helpful. I stumbled around on this for quite a while until I figured out how to use the augmenter. (Due to some oddities in my modules to work around Wicket. 🙄 And to continue using XML for some stuff.)

I'll probably have a pile of v8.3 alphas to share next week.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 19, 2025, 10:29pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/5 "2025-02-19T22:29:06Z")

</div>

There's a PR waiting on QA that breaks some Milo API stuff ([just a simple Search and Replace](https://github.com/eclipse-milo/milo/pull/1377)), and after that is merged and published I'll update the example with the API changes and see about adding config validation as well.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 19, 2025, 10:34pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/6 "2025-02-19T22:34:39Z")

</div>

I haven't looked hard, but haven't stumbled across the new methods for supplying documentation and defaults for config items. Annotations?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 19, 2025, 10:35pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/7 "2025-02-19T22:35:48Z")

</div>

> [@Kevin.Herron](#):
>
> breaks some Milo API

Actually saw that in my Github notifications earlier. No worries.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 19, 2025, 10:43pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/8 "2025-02-19T22:43:55Z")

</div>

Yeah, boatloads of ugly annotations that eventually make their way into the JSON schema for that resource type. But at least you don't have to create the schema by hand.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 19, 2025, 10:47pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/9 "2025-02-19T22:47:40Z")

</div>

Link?

Not sure our standards for "ugly" are widely shared, fwiw.

Oh, is there support for option strings?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 19, 2025, 10:50pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/10 "2025-02-19T22:50:04Z")

</div>

I don't have a link, but here's an example:

```python
    public record Connectivity(
        @FormCategory("CONNECTIVITY")
        @FormField(FormFieldType.TEXT)
        @Label("Hostname *")
        @Required
        @Description("Hostname/IP address of the device.")
        String hostname,

        @FormCategory("CONNECTIVITY")
        @Label("Port *")
        @FormField(FormFieldType.NUMBER)
        @Maximum("65535")
        @Minimum("1")
        @DefaultValue("102")
        @Required
        @Description("Port to connect to.")
        int port,

        @FormCategory("CONNECTIVITY")
        @Label("Timeout *")
        @FormField(FormFieldType.NUMBER)
        @DefaultValue("30000")
        @Required
        @Description("Maximum amount of time to wait for a response in milliseconds.")
        Duration timeout
    ) {
        private void validate(ValidationErrors.Builder errors) {
            errors.requireNotBlank(
                "connectivity.hostname",
                hostname
            ).checkField(
                port >= 1 && port <= 65535,
                "connectivity.port",
                "Port must be between 1 and 65535"
            ).checkField(
                timeout.toMillis() >= 1 && timeout.toMillis() <= Integer.MAX_VALUE,
                "connectivity.timeout",
                "Timeout must not be negative and must be less than 24 days" // Int.MAX_VALUE in millis is ~24 days
            );
        }

    }

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 23, 2025, 4:55pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/11 "2025-02-23T16:55:26Z")

</div>

I have my EtherNet/IP module building and installing cleanly, but it won't show a Web UI when I try to create an instance.

```python
Web UI Component type not found

```

I tried to build and install your modbus server as a point of comparison, but it utterly crashes my v83 install. With this:

```python
INFO | jvm 1 | 2025/02/23 16:46:51 | E [C.BasicExecutionEngine] [16:46:51.135]: One-shot task com.inductiveautomation.ignition.gateway.IgnitionGateway$$Lambda$616/0x00000001003f2c10@449faeef threw uncaught exception. 
INFO | jvm 1 | 2025/02/23 16:46:51 | java.lang.AbstractMethodError: Missing implementation of resolved method 'abstract java.util.List getDeviceExtensionPoints()' of abstract class com.inductiveautomation.ignition.gateway.opcua.server.api.AbstractDeviceModuleHook.
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.opcua.server.api.AbstractDeviceModuleHook.getExtensionPoints(AbstractDeviceModuleHook.java:53)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.lambda$getExtensionPoints$16(ModuleManagerImpl.java:652)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline$7$1.accept(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.stream.ReferencePipeline.toList(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.modules.ModuleManagerImpl.getExtensionPoints(ModuleManagerImpl.java:654)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.config.ImmutableExtensionPointCollection.build(ImmutableExtensionPointCollection.java:83)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.config.ImmutableExtensionPointCollection.build(ImmutableExtensionPointCollection.java:63)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.secrets.SecretProviderManagerImpl.<init>(SecretProviderManagerImpl.java:57)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.IgnitionGateway.startupInternal(IgnitionGateway.java:920)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.redundancy.RedundancyManagerImpl.startup(RedundancyManagerImpl.java:332)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.IgnitionGateway.initRedundancy(IgnitionGateway.java:751)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.gateway.IgnitionGateway.lambda$initInternal$2(IgnitionGateway.java:675)
INFO | jvm 1 | 2025/02/23 16:46:51 | at com.inductiveautomation.ignition.common.execution.impl.BasicExecutionEngine$ThrowableCatchingRunnable.run(BasicExecutionEngine.java:550)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
INFO | jvm 1 | 2025/02/23 16:46:51 | at java.base/java.lang.Thread.run(Unknown Source)

```

Hints?

(I **did** upgrade to the latest deployable, which has Thursday's date on its files.)

Edit: Rebuilt the Modbus Server and it doesn't crash now. 🤷‍♂️

However, it also gets `Web UI Component type not found`

I guess there's still some missing infrastructure?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 23, 2025, 5:40pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/12 "2025-02-23T17:40:12Z")

</div>

Perhaps, I have no idea how the web stuff hooks up yet, or even if it’s a different story for third vs first party modules. I’ll have to look into this a bit.

I’ve only verified my Modbus Server driver compiles for 8.3 at this point.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 23, 2025, 5:44pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/13 "2025-02-23T17:44:38Z")

</div>

Fair enough. I'll get all of my modules building and cleanly installing for now. Two down, five to go. (Plus a few non-critical ones.)

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 23, 2025, 8:18pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/14 "2025-02-23T20:18:26Z")

</div>

I had a quick look, and it's fairly trivial if all you need is an auto-generated config page for your device extension point.

See [~ basic extension point config UI · kevinherron/modbus-server-driver@1eafe2a · GitHub](https://github.com/kevinherron/modbus-server-driver/commit/1eafe2a237a2ab2b95abe82b43966bf050e568db)

Adding custom UI seems a little more involved, but it looks like it begins with:

1. define some custom JS file you want to register:

```python
private static final SystemJsModule JS_MODULE = new SystemJsModule(
  MODULE_ID, "/res/mitsubishi/js/web-ui/driver-mitsubishi.js");

```

1. register it in `GatewayModuleHook::setup`:

```python
context.getWebResourceManager().getSystemJsModuleRegistry().add(JS_MODULE);

```

1. maybe some kind of menu action returned from your impl of `DeviceExtensionPoint`?

```python
public List<MenuAction> getMenuActions() {
    return List.of(new MenuAction(
        "Addresses",
        "Device Configuration",
        new ReactComponentInfo("DeviceConfiguration", JS_MODULE))
    );
}

```

as for what is actually expected of your JS file... no idea. Probably some kind of react BS. I'll have to talk to some front end guys to see where we are at with an SDK example I guess.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 23, 2025, 8:36pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/15 "2025-02-23T20:36:44Z")

</div>

> [@Kevin.Herron](#):
>
> is an auto-generated config page

For most, this will do.

> [@Kevin.Herron](#):
>
> custom UI seems a little more involved

I do want this for a few.

> [@Kevin.Herron](#):
>
> `getSystemJsModuleRegistry()`

I saw hints of that while spelunking.

> [@Kevin.Herron](#):
>
> `getMenuActions()`

Huh. Missed that entirely. Will poke around some more.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 25, 2025, 6:55pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/16 "2025-02-25T18:55:38Z")

</div>

Made progress, and now have config UIs showing up for both your module and mine.

However. Neither actually works. Unlike in v8.1, my `Device` implementation is getting called to browse/gather nodes that do not belong to my driver, and to read attributes that don't exist for variable nodes (`UserExecutable` and `EventNotifier`, in particular), and I can't seem get past that to get to a working subscription item.

(Your modbus server driver makes folders on browse where I expected variables, like `HR0`.)

(Your Logix driver is also timing out on browse.)

Sigh. Time for a break.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 25, 2025, 7:06pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/17 "2025-02-25T19:06:00Z")

</div>

> [@pturmel](#):
>
> and to read attributes that don't exist for variable nodes (`UserExecutable` and `EventNotifier`, in particular)

This is expected, there are some places in the server implementation where attributes are read indiscriminately before the NodeClass is known.

> <https://github.com/eclipse-milo/milo/blob/580bc5bbf1cfc9c60778a4fea4086c22ce97c3bd/opc-ua-sdk/sdk-server/src/main/java/org/eclipse/milo/opcua/sdk/server/subscriptions/SubscriptionManager.java#L867-L889>

> <https://github.com/eclipse-milo/milo/blob/580bc5bbf1cfc9c60778a4fea4086c22ce97c3bd/opc-ua-sdk/sdk-server/src/main/java/org/eclipse/milo/opcua/sdk/server/servicesets/impl/DefaultAccessController.java#L550-L575>

> [@pturmel](#):
>
> my `Device` implementation is getting called to browse/gather nodes that do not belong to my driver,

A call to `gather` is expected - gather is called when some Node you don't control is being browsed to see if you have a reference to contribute. `browse` should only get called for Nodes that pass the `AddressSpaceFilter`, though... I think.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 25, 2025, 7:07pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/18 "2025-02-25T19:07:27Z")

</div>

> [@Kevin.Herron](#):
>
> attributes are read indiscriminately

What should I return for those?

Yes, I found those Milo locations from my backtraces. I tried returning nulls in the `DataValue`'s `Variant`, but to no effect. 🤷‍♂️

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [February 25, 2025, 7:09pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/19 "2025-02-25T19:09:41Z")

</div>

Bad\_AttributeIdInvalid I think.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [February 25, 2025, 7:09pm UTC](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028/20 "2025-02-25T19:09:59Z")

</div>

I tried that first. Boom.

[Next page](https://forum.inductiveautomation.com/t/idbmigrationstrategy-for-device-extensionpoints/99028.md?page=2)
