Ignition Extensions - Convenience utilities for advanced users

Just want to give back to the community a little bit since you all have helped me so much. I know it's something I specifically asked for but maybe it will help someone else too - a way to take a dataset and convert it to code that would generate an identical dataset. I am using this so I can take datasets that are in windows and convert it to code for my testing functions easily (I have a lot of datasets to do it for)

def makeToDatasetScriptFromDS(ds):
	columnNames = [str(column) for column in ds.columnNames]
	rowData = []
	for cur_row in range(ds.getRowCount()):
		row = []
		for cur_col in range(ds.getColumnCount()):
			value = ds.getValueAt(cur_row, cur_col)
			if isinstance(value, unicode) or isinstance(value, str):
				value = str(value)
			row.append(value)
		rowData.append(row)
	code = 'system.dataset.toDataSet(%s, %s)'%(columnNames, rowData)
	return code

It works pretty well for me and the types seem to come through as well at least on my initial tests which is nice. I'm sure there is room for improvement but this works well enough for me atm.

5 Likes

You should be able to change this line to:
if isinstance(value, (unicode, str)):
or even
if isinstance(value, basestring):

2 Likes

Hi Paul,

Thanks for this function, this is what I want for a really long time. I use this In my table so user can add their own formula in new column.
I have a request here to improve usecase. As the number of column is unkown for each table, if we pass the varaible arg as an dict like {"var1": 12, "var2": 67,...} is much more easy for us to make the code totally dynamic.

 system.util.evalExpression("123 + {var1} + {var2}", {"var1": 12, "var2": 67,...})

You can do that already - just use double ** to "spread" the dict into keyword arguments:
system.util.evalExpression("123 + {var1} + {var2}", **{"var1": 12, "var2": 67,...})

8 Likes

Hey @PGriffith, is this something you can fix in an extension module function? (or rather, provide a function which converts it to something more useful - I actually thought system.util.deepCopy would do this, but it doesn't seem like it) (I don't really want to have to upgrade our customer's site just to fix this once it's fixed in the product itself)

Wondering if anyone would find an expression function schedule useful:
schedule(cron, onValue=1, offValue=0)

Which just pulses momentarily whenever the schedule is met.

2 Likes

Probably... right now we create a flasher client tag to help synchronize animated objects on the screen that flash on alarm. Currently, we use

toMillis(now(1000)) % 2000 < 1000

in a client tag to do a 1 sec flasher which I think may have come from @nminchin but I'm not 100% sure lol.

Would this expression be more efficient or worse?

schedule would probably be less efficient, but I could see a periodic(rate, onValue=1, offValue=0) function also. Name TBD.

4 Likes

:raised_hand: Ooh! me! me!

1 Like

I'm definitely finding the system.dataset.builder function extremely useful at the moment! I don't know what I would do without this module tbh, I've come to rely on so many of its functions :face_with_spiral_eyes: kudos @PGriffith !

4 Likes

@PGriffith Am I wrong or is the comment for the filter method wrong in the OP?

system.dataset.filter(dataset, filter): Dataset

Runs filter on every row of dataset. filter will be invoked with keyword arguments, per column in dataset. Return True to keep the column in the output, or False to omit it.

vs.

filter: a function to run on each row. Will be called with keyword arguments matching column names. The first argument will be named 'row' and is the row index. Return True to keep the row in the output dataset.

1 Like

0.7.0 available now:

Adds:

  • anyOf, allOf, noneOf expression functions that take variable arguments. Returns the logical predicate across the provided arguments; beware of vacuous truth if called with no arguments!
  • A simple, experimental tag history servlet which streams results. Available at http://$gatewayAddress/system/history-extension, accepts path (any number), startDate, endDate, returnSize, aggregationMode, and aliases (a comma-separated list) as parameters.
  • An experimental system.tag.getLocalConfiguration(basePath, recursive) function that works in the gateway scope, to retrieve only the locally defined configuration on any tag(s).
3 Likes

Hey Paul, how would I return this as a string so I can print to perspective.print?

I think I know how... I can do this:

class p_out():
   def write(self, string):
      system.perspective.print(string)

op = p_out()
system.dataset.print('bob', output=op)
2 Likes

Another, less efficient option, would be to use a StringIO object to buffer the print and then write it to system.perspective.print.

At the risk of sounding like an idiot, is there a possibility of these features becoming available in ignition at some point? If they are as useful as this thread shows then surely they should be made available to everyone? I wonder what happens 5 years down the track and another party is trying to maintain an ignition system that uses these features and documentation being limited or difficult to find.

2 Likes

To add to this, I understand this is a third party module created by Paul, but if we're creating functions that are in the realm of possibility for the base ignition platform, shouldn't these just be developed into ignition? I also wonder what happens when features get added in here that really should be in the base ignition and the platform ends up with hidden knowledge of "yeah you should really install Ignition Extensions for any new project".

1 Like

I do agree with you here. I think a lot of these functions should make it into Ignition. The good thing however with this module is that Paul can very kindly add requested functions usually a lot faster than these would be added into Ignition. Into Ignition, it has to go through all of IA's processes for selecting things to add, then through dev and rigorous testing. This module bypasses all of that, but the changes made I would assume are extremely unlikely to break something.

3 Likes

That is definitely a major blessing of this module, but I think it's also a curse. If all of the QA is being bypassed and we can all get our delicious candy straight away I can see this getting out of hand quite quickly as we decide to get new features through this process rather than going through the channels that IA already has.

1 Like

these arent really new features, they are just convenient scripts

4 Likes

I think you’ve got a good instinct here.

I don’t know if Paul just wasn’t around yet or doesn’t remember, but I’ve been watching with a mix of amusement and dismay as this module of his follows a parallel path to the “IA Labs” module horror show. The only difference is instead of a bunch of Design Services guys adding functions whenever they needed it for a project it’s Paul responding to customers, and hopefully the quality is a little better.

But it’s still receiving no design review, no QA, no promise of ever being integrated into Ignition, and ya’ll are getting hooked and adding this as a dependency anyway.

3 Likes