Ignition Extensions - Convenience utilities for advanced users

That will be sweet.

1 Like

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.

3 Likes

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.

How about a system.dataset.fromExcel function?

7 Likes

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

3 Likes

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

9 Likes

:heart_eyes::heart_eyes: Thank you!

Uh oh

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 :drooling_face: The only trouble is, my keyboard is now covered in drool

2 Likes

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

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)

This should work on all 8.1.0+ versions.

1 Like

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

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 :confused: )

*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

2 Likes

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:

# 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

cfg = system.tag.getConfiguration(...)
1 Like

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 :man_facepalming:

10 Likes

Heavily inspired by @JordanCClark's existing function:

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.

5 Likes

0.5.0 released, adds system.dataset.fromExcel:

4 Likes

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.

1 Like

Sure: Expose `DatasetBuilder` · Issue #13 · IgnitionModuleDevelopmentCommunity/ignition-extensions · GitHub

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

3 Likes

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

9 Likes

Yes, please.

6 Likes