# Ignition Extensions - Convenience utilities for advanced users

**URL:** <https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615>\
**Category:** 3rd Party Modules\
**Created:** [September 28, 2022, 6:41pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615 "2022-09-28T18:41:11Z")\
**Posts on this page:** 20\
**Page:** 2

<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:** [October 14, 2022, 3:52pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/21 "2022-10-14T15:52:54Z")

</div>

> [@ynejati](#):
>
> you will be able to select a color picker as an input type when you build out your forms

That will be sweet.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 14, 2022, 4:34pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/22 "2022-10-14T16:34:46Z")

</div>

Back to our regularly scheduled programming...

I'm not sure what the API for a subprocess-like utility should be.  
You mentioned `subprocess.communicate`, but that's actually blocking.

Maybe two top-level functions, following the existing pattern:  
`system.util.runBlocking`  
`system.util.runAsync`

Where `runBlocking` returns a `ProcessResult` or something similar with information about the run including `stdout` and `stderr`.  
`runAsync` would return a CompletableFuture wrapper, with maybe a method to inject data into stdin? Or you could `runAsync().wait()` and get the `ProcessResult`?

I'd have to think about the API a little more. If it gets somewhere stable enough, maybe I'll push to make it first party; I think that's something lots of people would find useful.

---

<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:** [October 14, 2022, 4:41pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/23 "2022-10-14T16:41:57Z")

</div>

> [@paul-griffith](#):
>
> You mentioned `subprocess.communicate`, but that's actually blocking.

Yes, but explicitly stated this analog would return a future. In both cases allow supplying an optional InputStream for stdin. That could be something that dynamically generates content if necessary. Accumulate stdout and stderr into ByteArrayOutputStream instances, then deliver the byte arrays in a tuple to the future.

I'm not opposed to a blocking version.

---

<div class="post-metadata">

**Author:** ![bmusson](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bmusson/32/87530_2.png) [@bmusson](https://forum.inductiveautomation.com/u/bmusson)\
**Post date:** [October 17, 2022, 9:42pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/24 "2022-10-17T21:42:49Z")

</div>

How about a `system.dataset.fromExcel` function?

> [@Copying an Excel file to a table's dataset](https://forum.inductiveautomation.com/t/copying-a-excel-file-to-a-tables-dataset/34942/13):
>
> Got my paperwork done early, so here's a sample to read from an Excel file EDIT: Reworked a bit to work with both .xls and .xlsx files EDIT: Modified to properly cast data types EDIT: Added handler for formula cell types EDIT: Added parameters to optionally specify sheet, row, and column numbers Reading back a file created from my other example: def excelToDataSet(fileName, hasHeaders = False, sheetNum = 0, firstRow = None, lastRow = None, firstCol = None, lastCol = None): import org.apac…

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 18, 2022, 2:20am UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/25 "2022-10-18T02:20:42Z")

</div>

> [@\[FEATURE\] Add expression function to check if tag is valid (exists and not disabled)](https://forum.inductiveautomation.com/t/feature-add-expression-function-to-check-if-tag-is-valid-exists-and-not-disabled/61192):
>
> I should have asked for this a very long time ago to be honest… I would like a function to check if a tag is valid. What I mean by this, is that it both exists and is not disabled. No other qualities should affect the value returned. Often I will create templates / view templates that can accommodate multiple UDT definitions and/or instance configurations. E.g. differences in the tags available between similar UDT defs or tags that are disabled in particular instances of the UDTs. To accommoda…

Can you pleeease add this one 🙂 This would make my life vastly better (and get rid of a large number of horrible looking expressions)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 18, 2022, 6:19pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/26 "2022-10-18T18:19:14Z")

</div>

0.4.0 out now, adds `system.util.evalExpression` and `isAvailable`:

> **[Release 0.4.0 · IgnitionModuleDevelopmentCommunity/ignition-extensions](https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/releases/tag/0.4.0)**
>
> Commits
> 
> 1689845: Add system.util.evalExpression (Paul Griffith) #7
> 872ce4c: Add isAvailable expression function (Paul Griffith) #9

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 18, 2022, 8:45pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/27 "2022-10-18T20:45:41Z")

</div>

😍😍 Thank you!

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 19, 2022, 1:23am UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/28 "2022-10-19T01:23:51Z")

</div>

Uh oh

```auto
ModuleLoadException: Hook class "org.imdc.extensions.designer.DesignerHook" not found for module "Ignition Extensions".
	caused by ClassNotFoundException: org.imdc.extensions.designer.DesignerHook

Ignition v8.1.21 (b2022092908)
Java: Azul Systems, Inc. 11.0.15

```

It looks like they're still in there though, and `isAvailable` is so good 🤤 The only trouble is, my keyboard is now covered in drool

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 19, 2022, 2:30pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/29 "2022-10-19T14:30:37Z")

</div>

Huh, that's no good. It looks like the built artifact has everything... I'll do some digging today.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 19, 2022, 8:39pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/30 "2022-10-19T20:39:05Z")

</div>

Also, I know I might be asking a lot, but do you know what sort of versions this module is compatible with? Is it most of the 8.1.x versions? (definitely not 7.9, and don't really care about 8.0)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 19, 2022, 8:42pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/31 "2022-10-19T20:42:16Z")

</div>

This should work on all 8.1.0+ versions.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 19, 2022, 11:08pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/32 "2022-10-19T23:08:40Z")

</div>

Is it possible to "fix" this one up as well?

> [@System.tag.configure incorrectly overriding multiple UDT tag properties](https://forum.inductiveautomation.com/t/system-tag-configure-incorrectly-overriding-multiple-udt-tag-properties/40593):
>
> I need a quick way to programmatically override properties on certain tags inside a UDT. For instance, setting a custom description for multiple iterations of the same device. The solution I’ve come up with is to read the tag configuration using system.tag.getConfiguration and then search through the result and modify the ‘value’ property for the tag in question. Then I pass the modified configuration back into system.tag.configure. I’m able to successfully change the value I want, however, …

I'm sure I had an idea somewhere, but my suggestion was to add an argument into the `system.tag.getConfiguration` function to return only the config stored on the tags themselves, not from parent settings such as non-defaults configured in the UDT definition.

E.g. an arg to return exactly the properties that copying the JSON of a tag returns\*. Otherwise, if you use `system.tag.configure` with the results from `getConfiguration` which contains UDT instances, you end up overriding a whole bunch of properties on tags that you _ **really** _ didn't want to, that are defined in the UDT Definition! Thus essentially un-binding the UDT instances from the UDT definitions which is (hopefully) never what you want.

Ideally this fix should go into main Ignition, but for now it would be awesome to have this right now (I forgot about this `feature` yesterday and added property overrides to many many UDT instance tags which I now need to painfully remove 😕 )

\*Coincidentally, it would also be nice to have a tag context option to return what `getConfiguration` currently returns alongside the standard Copy JSON function (i.e. _return_ all of the non-defaults configured on the tag, both on the tag in the current scope as well as within its UDT definition)  
E.g.  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/2/127e9488dc4b5aa970ed8e606878e376e8b16006.png)

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [October 20, 2022, 12:17am UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/33 "2022-10-20T00:17:53Z")

</div>

Also, just playing with `system.util.deepCopy()` and found something strange. I have results from `system.tag.getConfiguration` that I want to convert to a JSON string with jsonEncode. When I run a single item from cfg through deepCopy, I get these results compared with the base cfg object:

```auto
# With system.util.deepCopy(cfg):
[{u'dataType': Boolean, u'documentation': [u'S', u't', u'a', u't', u'u', u's', u' ', u'o', u'f', u' ', u't', u'h', u'e', u' ', u'M', u'o', u'd', u'e', u' ', u's', u'w', u'i', u't', u'c', u'h', u' ', u'a', u't', u' ', u't', u'h', u'e', u' ', u'p', u'u', u'm', u'p'], u'path': [default]Wynns/Winery/WWTP/Main/Aerators/A1/Mode Switch Auto, u'opcItemPath': {bindType=parameter, binding=ns=1;s=[{PLCName}]Live[{ModesArrayIndex}].{AutoBitNumber}}, u'tagType': AtomicTag, u'name': [u'M', u'o', u'd', u'e', u' ', u'S', u'w', u'i', u't', u'c', u'h', u' ', u'A', u'u', u't', u'o'], u'opcServer': [u'I', u'g', u'n', u'i', u't', u'i', u'o', u'n', u' ', u'O', u'P', u'C', u'-', u'U', u'A', u' ', u'S', u'e', u'r', u'v', u'e', u'r'], u'valueSource': [u'o', u'p', u'c']}]

# Base cfg object:
[{u'dataType': Boolean, u'documentation': u'Status of the Mode switch at the pump', u'path': [default]Wynns/Winery/WWTP/Main/Aerators/A1/Mode Switch Auto, u'opcItemPath': {bindType=parameter, binding=ns=1;s=[{PLCName}]Live[{ModesArrayIndex}].{AutoBitNumber}}, u'tagType': AtomicTag, u'name': u'Mode Switch Auto', u'opcServer': u'Ignition OPC-UA Server', u'valueSource': u'opc'}]

```

deepCopy is converting strings into arrays of chars

Where

```auto
cfg = system.tag.getConfiguration(...)

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 20, 2022, 2:58am UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/34 "2022-10-20T02:58:39Z")

</div>

> **[Release 0.4.1 · IgnitionModuleDevelopmentCommunity/ignition-extensions](https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/releases/tag/0.4.1)**
>
> Commits
> 
> 125d67b: Fix quiet NoSuchElementException in printDataset of empty dataset (Paul Griffith)
> 955780d: Add test case and optimize columnWidths data structure (Paul Griffith)
> 9eb2c2d: Fix issu...

0.4.1 out now - fixes the issue with expanding strings, and another bug I noticed where `system.dataset.print` would blow up on empty dataset 🤦‍♂️

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 20, 2022, 10:28pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/35 "2022-10-20T22:28:10Z")

</div>

> [@bmusson](#):
>
> How about a `system.dataset.fromExcel` function?

Heavily inspired by @JordanCClark's existing function:

> <https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/pull/12>
>
> \* Add includeTypes argument to system.dataset.print
> 
> Fixes #10

Could use some additional testing (any test files anyone is willing to share would be helpful) before I merge.  
Also, a TODO for myself: add a way to download SNAPSHOT modules of PR builds. That'd be nifty.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 31, 2022, 4:44pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/36 "2022-10-31T16:44:23Z")

</div>

0.5.0 released, adds `system.dataset.fromExcel`:

> **[Release 0.5.0 · IgnitionModuleDevelopmentCommunity/ignition-extensions](https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/releases/tag/0.5.0)**
>
> Commits
> 
> c403cf7: Add system.dataset.fromExcel (#12) (Paul Griffith) #12

---

<div class="post-metadata">

**Author:** ![bkarabinchak.psi](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bkarabinchak.psi/32/83567_2.png) [@bkarabinchak.psi](https://forum.inductiveautomation.com/u/bkarabinchak.psi)\
**Post date:** [October 31, 2022, 5:01pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/37 "2022-10-31T17:01:07Z")

</div>

In terms of `system.dataset` functions, how about a convenience function for making a dataset? Right now to make new dataset with expressly defined types, you need to from java import DatasetBuilder and then import any java datatypes to define the column type.

What I think would be neat and helpful (and more readable to my controls engineer coworkers who rarely touch jython) would be something like  
`newDs = sytem.dataset.buildDS(['colName1','colName2',..],[int,str,...])` where the python typing of `int` maps to `java.lang.Integer`, str to `java.lang.String` etc

I don't know that's my first mockup of it, but just an easier way to build a dataset with specified types.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 31, 2022, 5:22pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/38 "2022-10-31T17:22:19Z")

</div>

Sure: [Expose `DatasetBuilder` · Issue #13 · IgnitionModuleDevelopmentCommunity/ignition-extensions · GitHub](https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/issues/13)

Have to do some experimenting on how types will pass through.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [November 3, 2022, 3:30pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/39 "2022-11-03T15:30:08Z")

</div>

> <https://github.com/IgnitionModuleDevelopmentCommunity/ignition-extensions/issues/14>
>
> \#### \`system.dataset.equals(dataset, dataset)\`
> 
> \#### \`system.dataset.valuesEqu…al(dataset, dataset)\`
> 
> \#### \`system.dataset.columnsEqual(dataset, dataset, ignoreCase=False, includeTypes=True)\`
> 
> May need some input from end users about what would be most useful.

Need some input on whether these proposed functions would be useful to folks.

---

<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 3, 2022, 3:46pm UTC](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615/40 "2022-11-03T15:46:28Z")

</div>

> [@paul-griffith](#):
>
> would be useful

Yes, please.

[Previous page](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615.md?page=1)

[Next page](https://forum.inductiveautomation.com/t/ignition-extensions-convenience-utilities-for-advanced-users/64615.md?page=3)
