Show array of strings

Hi,

I have a ring buffer I'm using as an event logger that I write to in the PLC. It has severity as an int, message, severity and time as strings. I'm wondering what's a good way to show this in vision with conditional color formatting, hiding acknowledged lines, etc. Any ideas?

Thanks!

A table? Probably a power table, for more advanced formatting options?

You "just" need to adapt your data into Ignition's dataset format, which basically just requires you to spread the rows into however many columns you need.

If you can avoid using strings, avoid using strings. They are murder on PLC connection optimization.

@pturmel Yikes that sounds terrifying. Even for Beckhoff? How bad does a big array of strings make things?

Workload is bytes. Make them few, and organized consecutively in the target PLC. I would use an event sequence for handshaking, with message code and 64-bit long integer timestamp. (Like from Rockwell's Wallclock GSV instruction.) Embed the severity in the message code.

I have zero Beckhoff experience at this time, but if you are using its OPC server, you are adding even more performance-sucking overhead.

Is the overhead on the PLC's memory or CPU or what exactly? I'm looking for a way to check the performance and see how badly impacted it is by this buffer on the OPCUA.

Not sure about Beckhoff, but probably PLC CPU. I know that it certainly would be CPU (Class 3 message processing) on a Rockwell PLC.

Ignition doesn't have any native functionality to convert your structure to a table-compatible format. I created an add-on module to help with cases like this, but what you are after isn't simple. (Natively, Ignition expressions cannot perform any iteration.)

I would use a custom property to construct a dataset of the tagpaths you need. Something like this:

unionAll(
	asMap('tagpath', 'str'),
	flatten(
		forEach(
			100,
			forEach(
				asList('eSeverity', 'sMessage', 'sSource', 'sTimestamp'),
				asList( // unionAll needs lists of lists.
					stringFormat(
						'[someProvider]stEvents/stEvents_%d_/%s',
						idx(1),
						it()
					)
				)
			)
		)
	)
)

Then I'd use tags() to monitor the live values of all of those, and wrap it in grouping to yield four columns. Something like this:

unionAll(
	asMap(
		'tagFolder', 'str',
		'eSeverity', 'I',
		'sMessage', 'str',
		'sSource', 'str',
		'sTimestamp', 'str'
	),
	forEach(
		groupBy( // extract the four leaf tags together as groups
			forceQuality(
				tags({Root Container.eventsTags}), // yields two-column dataset, path and value
				192
			),
			left( // trim off the last name of the path
				it()[0], // path column
				lastIndexOf(it()[0], '/')
			)
		),
		transform(
			lastIndexOf(it()[0], '/'), // grouping key output
			asMap(  // unionAll can also take a map with keys==colName
				asPairs(
					asMap('tagFolder', left(it()[0], value())),
					forEach(
						it()[1], // Nested dataset
						substring(it()[0], value()+1), // path from row of nested DS
						it()[1] // value from row of nested DS
					)
				)
			)
		)
	)
)

Then you can use my where() and/or orderBy() functions for additional operations.

(Untested. Tweak to suit.)