# Automation Professionals' Integration Toolkit Module

**URL:** <https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934>\
**Category:** 3rd Party Modules\
**Tags:** perspective, ignition81, vision, scripting, ignition83\
**Created:** [June 13, 2023, 12:42am UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934 "2023-06-13T00:42:35Z")\
**Posts on this page:** 20\
**Page:** 7

<div class="post-metadata">

**Author:** ![Mathias\_Melling](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/mathias_melling/32/67682_2.png) [@Mathias\_Melling](https://forum.inductiveautomation.com/u/Mathias_Melling)\
**Post date:** [April 25, 2024, 8:19pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/121 "2024-04-25T20:19:16Z")

</div>

That's very helpful.  
Thanks a lot Phil!

---

<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:** [May 14, 2024, 8:54pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/122 "2024-05-14T20:54:44Z")

</div>

Automation Professionals is pleased to announce a **beta** release of this Integration Toolkit:

For Ignition v8.1+, as usual: ~~v2.0.16.241352008~~

This beta introduces a number of new **scripting** features:

- Inspired by @Mathias_Melling above, the souped-up dictionary functionality of the `BindableVarMap` data type has been factored out, as [system.util.VarMap](https://www.automation-pros.com/toolkit/doc/scripting.html#varmap). Serializable to the extent its contents are serializable, just like dictionaries, and safe for use with persistence. 😁

- The `PythonAsJavaException` wrapper from my `later.py` script has been re-implemented in java as `PyAsJavaException`, exposed via the helper function [system.reflect.asThrowable()](https://www.automation-pros.com/toolkit/doc/reflection.html#asthrowable). This implementation extracts the package to include with the function or class+method on each line of the resulting traceback. And it is serializable, suitable for inclusion in gateway network or designer/client messaging.

- As a side-effect of the work on `PyAsJavaException`, [system.reflect.getModulePath()](https://www.automation-pros.com/toolkit/doc/reflection.html#getmodulepath) is available for use within project library scripts. Particularly useful when naming logger instances.

And **finally** , something that has been percolating for a while now:

- A solution to testing gateway code in the designer script console, in the form of a **decorator** for project library functions: [@system.util.runInGateway](https://www.automation-pros.com/toolkit/doc/utility.html#runingateway). Place this decorator in front of your gateway-scope-only library function and calls from Designer or Vision Client scopes will be automatically turned into an RPC call to the gateway instance of the code, and that instance's return value passed back to the calling scope.

The above can be approximated with message handlers on a per-project basis, but that approximation suffers from significant extra opportunities for errors, and lack of parallelism at scale.

Try something like this code snippet, perhaps in `toolkit`:

```python
logger = system.util.getLogger(system.util.getProjectName() + '.' + system.reflect.getModulePath())

@system.util.runInGateway
def gwLoggerTest(someMessage):
	logger.info(someMessage)
	return "Logged: " + someMessage

```

(Remember to save your project.) Then try this in the designer script console:

```python
print toolkit.gwLoggerTest("Hold my beer!")

```

Then go look at your gateway logs. 😁

{ It is a beta because travel and client crises have limited testing time... }

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [May 30, 2024, 7:59pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/123 "2024-05-30T19:59:57Z")

</div>

I'm running into an error trying to merge a dataset of tag paths and a comma separated list of tag paths into a single dataset in Vision. Ignition version 8.1.25.

If I attempt to use any string expression functions(such as `trim` or `toStr`) while iterating though the result of `split`, I'll get an error saying: `com.inductiveautomation.ignition.common.expressions.ExpressionException: Unsupported type in rowSource: class java.lang.String `. If I remove the string expression functions, the binding will compile and run without issue.

> **Union All error**
>
> ```python
> java.lang.Exception: Error executing expression binding on
> MagicPlot.MagicPlot.plotTagPaths
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:92)
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.startup(ExpressionPropertyAdapter.java:113)
> at com.inductiveautomation.factorypmi.application.binding.DefaultInteractionController.setPropertyAdapter(DefaultInteractionController.java:219)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.bind(ExpressionConfigurator.java:246)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.tryCommit(ExpressionConfigurator.java:192)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor$EditorParent.tryCommit(ConfiguratorMultiplexor.java:398)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor.tryCommit(ConfiguratorMultiplexor.java:546)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.doOK(DynamicOptsDialog.java:95)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog$1.actionPerformed(DynamicOptsDialog.java:64)
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.Component.processEvent(Unknown Source)
> at java.desktop/java.awt.Container.processEvent(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Native Method)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Native Method)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
> at com.inductiveautomation.snap.swing.RibsEventQueue.dispatchEvent(RibsEventQueue.java:99)
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$2.run(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Native Method)
> at java.desktop/java.awt.WaitDispatchSupport.enter(Unknown Source)
> at java.desktop/java.awt.Dialog.show(Unknown Source)
> at java.desktop/java.awt.Component.show(Unknown Source)
> at java.desktop/java.awt.Component.setVisible(Unknown Source)
> at java.desktop/java.awt.Window.setVisible(Unknown Source)
> at java.desktop/java.awt.Dialog.setVisible(Unknown Source)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.showDialog(DynamicOptsDialog.java:163)
> at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.openBindingDialog(VisionDesignerImpl.java:1223)
> at com.inductiveautomation.factorypmi.designer.property.editors.bb.DynamicOptionsButton.actionPerformed(DynamicOptionsButton.java:37)
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
> at com.jidesoft.plaf.basic.BasicJideButtonListener.mouseReleased(Unknown Source)
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.Component.processEvent(Unknown Source)
> at java.desktop/java.awt.Container.processEvent(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicTableUI$Handler.repostEvent(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicTableUI$Handler.mouseReleased(Unknown Source)
> at com.jidesoft.swing.DelegateMouseInputListener.mouseReleased(Unknown Source)
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.Component.processEvent(Unknown Source)
> at java.desktop/java.awt.Container.processEvent(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Native Method)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Native Method)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
> at com.inductiveautomation.snap.swing.RibsEventQueue.dispatchEvent(RibsEventQueue.java:99)
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
> Caused by: com.inductiveautomation.ignition.common.expressions.ExpressionException: Unable to handle rowSource #2
> at com.automation_pros.simaids.expressions.UnionAll.execute(UnionAll.java:119)
> at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)
> at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:83)
> ... 104 common frames omitted
> Caused by: com.inductiveautomation.ignition.common.expressions.ExpressionException: Unsupported type in rowSource: class java.lang.String
> at com.automation_pros.simaids.expressions.UnionAll.buildWith(UnionAll.java:203)
> at com.automation_pros.simaids.expressions.UnionAll.execute(UnionAll.java:117)
> ... 107 common frames omitted
> 
> ```

> **Working Binding**
>
> ```python
> unionAll(
> asMap("tagPath", "str"),
> {MagicPlot.tagPaths},
> where(	
> if(
> {MagicPlot.tagPathList} != "",
> forEach(
> split({MagicPlot.tagPathList}, ","), it()
> ), asList(None)
> ), !isNull(it())
> )
> )
> 
> ```

> **Erroring binding**
>
> ```python
> unionAll(
> asMap("tagPath", "str"),
> {MagicPlot.tagPaths},
> where(	
> if(
> {MagicPlot.tagPathList} != "",
> forEach(
> split({MagicPlot.tagPathList}, ","), trim(it())
> ), asList(None)
> ), !isNull(it())
> )
> )
> 
> ```

tagpathList: `[default]OEE/Cell A/TestValueA, [default]OEE/Cell A/TestValueB`  
tagPaths Dataset:

```python
"#NAMES"
"tagPath"
"#TYPES"
"str"
"#ROWS","1"
"[default]OEE/Cell A/TestValuec"

```

---

<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:** [May 30, 2024, 8:05pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/124 "2024-05-30T20:05:41Z")

</div>

`split()` returns a multi-row dataset.

Iterating through a dataset supplies a single row to `it()` at a time. You cannot use `trim()` on the one-row dataset--it needs a string, and returns a string.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [May 30, 2024, 8:16pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/125 "2024-05-30T20:16:06Z")

</div>

I guess I got confused on what my iteration loop was returning and what union all was expecting. Changing the iteration through the results of split() to

```python
forEach(
	split({MagicPlot.tagPathList}, ","), asList(trim(it()[0]))
)

```

corrected the issue.

I was thinking that the error was coming from me doing string operations on a row value but it was actually coming from the union all expecting a list of lists, and was not happy it was finding a list of strings instead for the row values, along with me attempting to perform string operations on a dataset row instead of a value.

I keep forgetting that split returns a dataset and not a list. I think I'm assuming it behaves like python's `split`

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [July 10, 2024, 3:35pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/126 "2024-07-10T15:35:27Z")

</div>

I've run into this error, and I just can't make since of it, so I thought I would ask for some assistance.

> **Error**
>
> ```python
> java.lang.Exception: Error executing expression binding on
> Stop Occurances.Root Container.Table.data
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:92)
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.startup(ExpressionPropertyAdapter.java:113)
> at com.inductiveautomation.factorypmi.application.binding.DefaultInteractionController.setPropertyAdapter(DefaultInteractionController.java:248)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.bind(ExpressionConfigurator.java:246)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ExpressionConfigurator.tryCommit(ExpressionConfigurator.java:192)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor$EditorParent.tryCommit(ConfiguratorMultiplexor.java:398)
> at com.inductiveautomation.factorypmi.designer.property.configurators.ConfiguratorMultiplexor.tryCommit(ConfiguratorMultiplexor.java:546)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.doOK(DynamicOptsDialog.java:95)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog$1.actionPerformed(DynamicOptsDialog.java:64)
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.Component.processEvent(Unknown Source)
> at java.desktop/java.awt.Container.processEvent(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$2.run(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)
> at java.desktop/java.awt.WaitDispatchSupport.enter(Unknown Source)
> at java.desktop/java.awt.Dialog.show(Unknown Source)
> at java.desktop/java.awt.Component.show(Unknown Source)
> at java.desktop/java.awt.Component.setVisible(Unknown Source)
> at java.desktop/java.awt.Window.setVisible(Unknown Source)
> at java.desktop/java.awt.Dialog.setVisible(Unknown Source)
> at com.inductiveautomation.factorypmi.designer.property.configurators.DynamicOptsDialog.showDialog(DynamicOptsDialog.java:163)
> at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.openBindingDialog(VisionDesignerImpl.java:1236)
> at com.inductiveautomation.factorypmi.designer.property.editors.bb.DynamicOptionsButton.actionPerformed(DynamicOptionsButton.java:37)
> at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
> at java.desktop/javax.swing.DefaultButtonModel.setPressed(Unknown Source)
> at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
> at com.jidesoft.plaf.basic.BasicJideButtonListener.mouseReleased(Unknown Source)
> at java.desktop/java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
> at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
> at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.Component.processEvent(Unknown Source)
> at java.desktop/java.awt.Container.processEvent(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
> at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
> at java.base/java.security.AccessController.doPrivileged(Unknown Source)
> at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
> at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
> at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
> Caused by: com.inductiveautomation.ignition.common.expressions.ExpressionException: Error performing subscript operation
> at com.inductiveautomation.ignition.common.expressions.SubscriptExpression.execute(SubscriptExpression.java:126)
> at com.inductiveautomation.ignition.common.expressions.functions.BaseFunction.executeAll(BaseFunction.java:64)
> at com.automation_pros.simaids.expressions.SimpleIterator.execute(SimpleIterator.java:177)
> at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)
> at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
> at com.inductiveautomation.factorypmi.application.binding.ExpressionPropertyAdapter.runExpression(ExpressionPropertyAdapter.java:83)
> ... 90 more
> Caused by: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
> at java.base/jdk.internal.util.Preconditions.outOfBounds(Unknown Source)
> at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Unknown Source)
> at java.base/jdk.internal.util.Preconditions.checkIndex(Unknown Source)
> at java.base/java.util.Objects.checkIndex(Unknown Source)
> at java.base/java.util.ArrayList.get(Unknown Source)
> at com.automation_pros.simaids.expressions.SimpleIterator.getValue(SimpleIterator.java:56)
> at com.automation_pros.simaids.expressions.IteratorValue.execute(IteratorValue.java:40)
> at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)
> at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
> at com.inductiveautomation.ignition.common.expressions.SubscriptExpression.execute(SubscriptExpression.java:50)
> at com.inductiveautomation.ignition.common.expressions.SubscriptExpression.execute(SubscriptExpression.java:59)
> ... 95 more
> 
> Ignition v8.1.35 (b2023120517)
> Java: Azul Systems, Inc. 17.0.8
> 
> ```

I'm using the following expression to add a column to a dataset.

> **Expression**
>
> ```python
> selectStar(
> {Root Container.Processing Group.processingStopsTable.pivotedData},
> asMap(
> forEach(
> groupBy(
> {Root Container.Processing Group.processingStopsTable.last8HistoricalData},
> split(it()['path'],'_')[0]
> ),
> asList(
> case(
> it()[0],
> 'productlevel', 'No Product',
> 'cap', 'No Caps',
> 'empty', 'No Bottles',
> 'Packaging'
> ),
> sum(it()[1],'value')
> )
> )
> )[it()['Occurrence']],
> 'Last 8',
> 'I'
> )
> 
> ```

The internal expression works as expected and returns the following Map:

```python
{No Product=7.0,No Caps=29.0, Packaging=27.0, No Bottles=3.0}

```

The pivotedData dataset does have an "Occurrence" column.

> **pivotedData Dataset**
>
> ```python
> "#NAMES"
> "Occurrence","_7A","_8A","_9A","_10A","_11A","_12P","_1P","_2P","_3P","_4P","_5P","_6P","_7P","_8P","_9P","_10P","_11P","_12A","_1A","_2A","_3A","_4A","_5A","_6A"
> "#TYPES"
> "str","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I","I"
> "#ROWS","4"
> "No Product","0","0","1","0","0","1","0","0","0","0","2","0","2","0","0","1","0","1","0","0","1","1","4","1"
> "No Caps","2","8","0","0","0","1","1","1","0","0","0","0","1","1","1","0","1","16","8","7","4","5","3","0"
> "Packaging","6","2","6","1","0","0","2","4","1","11","0","0","6","0","4","4","2","5","1","1","2","4","6","4"
> "No Bottles","0","2","5","0","0","1","1","1","0","2","1","0","1","0","1","1","1","7","0","0","0","0","0","0"
> 
> ```

Any guidance is appreciated.

---

<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:** [July 10, 2024, 3:40pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/127 "2024-07-10T15:40:39Z")

</div>

The arguments to `selectStar()` changed along the way. Column info is now the 2nd argument, and following arguments are the column value expressions.

[https://www.automation-pros.com/toolkit/doc/datasets.html#selectstar](https://www.automation-pros.com/toolkit/doc/datasets.html#selectstar)

Consider also extracting that `groupBy()` and the asMap() around it into a subexpression via `transform()` and `value()` so it is only executed once, instead of for every row. Your row expression would then be just

```python
value()[it()['Occurrence']]

```

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [July 10, 2024, 3:46pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/128 "2024-07-10T15:46:23Z")

</div>

Sheesh, that was embarrassingly simple. I read the documentation like 10 times and missed the order completely.

As always, thanks for the quick response.

---

<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:** [July 11, 2024, 9:27pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/129 "2024-07-11T21:27:25Z")

</div>

Automation Professionals is pleased to announce the latest **production** release of the Integration Toolkit module:

For Ignition v8.1+: [v2.0.17.241921515](https://www.automation-pros.com/ignition/Integration%20Toolkit-2.0.17.241921515-v81.modl)

It fixes one bug found in the last beta, a race condition that broke initialization of the python VarMap monkey-patching. Without that, property access to dictionary keys did not work. This flaw showed up on high-end hardware that was running a very stripped-down Ignition instance. (Testing my spreadsheet-based tag tool. Almost ready, fwiw.)

Also, `system.reflect.getModulePath()` is the correct name, not `system.util.getModulePath()`. (I had to move it and didn't fix the example.)

---

<div class="post-metadata">

**Author:** ![Samuel\_Sueur](https://avatars.discourse-cdn.com/v4/letter/s/e36b37/32.png) [@Samuel\_Sueur](https://forum.inductiveautomation.com/u/Samuel_Sueur)\
**Post date:** [July 15, 2024, 5:59pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/130 "2024-07-15T17:59:05Z")

</div>

Apologies if this is not the right place, but I have to ask :

Your `tags` function seems like a necessity in the core Ignition functions, as myself and our different development teams are in dire need of such functionalities in our projects. Clients are more and more demanding and resorting to building a dynamic list of tag paths is becoming very common, or at least it would really speed up our development cycle...

And from what I've gathered, the alternatives to the `tags` function are clearly far from ideal (hidden table with embedded view or creating tons of bindings in advance...)

I know @pturmel isn't related to IA staff by any means, but it would be awesome if someone from IA could chime in and tell us whether this `tags` function behavior will be implemented one day, say in 8.3.0 ?

I think your work is amazing @pturmel but unfortunately our clients are generally reluctant to incorporating modules from 3rd parties, despite our best efforts to convince them to go the "3rd party" road.

---

<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:** [July 15, 2024, 6:02pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/131 "2024-07-15T18:02:53Z")

</div>

> [@Samuel\_Sueur](#):
>
> will be implemented one day

Maybe.

> [@Samuel\_Sueur](#):
>
> say in 8.3.0

Almost certainly not.

---

<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:** [July 15, 2024, 6:09pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/132 "2024-07-15T18:09:37Z")

</div>

> [@Samuel\_Sueur](#):
>
> generally reluctant to incorporating modules from 3rd parties

Sorry.

This module exists, and is _ **NOT** _ open source, _ **precisely** _ to break down this reluctance. I have no sympathy for it **at all**.

Once any organization accepts any of my modules in their systems, they tend to not be reluctant to use my other modules, where applicable. In other words, this module is what a general retailer would call a "loss leader"--a product provided at a loss whose purpose is to enable other sales. And my unpaid hours sunk into this module definitely counts as a loss.

If I get hit by a bus, my heirs have instructions to sell my IP to an entity that can and will carry it forward.

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [July 15, 2024, 6:15pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/133 "2024-07-15T18:15:25Z")

</div>

> [@pturmel](#):
>
> If I get hit by a bus, my heirs have instructions to sell my IP to an entity that can and will carry it forward.

That's always good to hear as I know I've heard that argument in terms of using an integrator with a team vs a sole contracted one-man-shop integrator.

---

<div class="post-metadata">

**Author:** ![Samuel\_Sueur](https://avatars.discourse-cdn.com/v4/letter/s/e36b37/32.png) [@Samuel\_Sueur](https://forum.inductiveautomation.com/u/Samuel_Sueur)\
**Post date:** [July 15, 2024, 6:17pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/134 "2024-07-15T18:17:04Z")

</div>

> [@pturmel](#):
>
> If I get hit by a bus, my heirs have instructions to sell my IP to an entity that can and will carry it forward.

Understood, this is an amazing argument we can now put forward when dealing with reluctant clients whose main argument is "but what happens afterwards when there's no one left to maintain this module?"

Again, I'm convinced your modules are top notch, thanks for your work.

---

<div class="post-metadata">

**Author:** ![dkhayes117](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dkhayes117/32/68344_2.png) [@dkhayes117](https://forum.inductiveautomation.com/u/dkhayes117)\
**Post date:** [August 5, 2024, 6:29pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/135 "2024-08-05T18:29:11Z")

</div>

@pturmel I'm trying to use `forEach()` to do cell color in a perspective table. I can get it to work for row background color, but I can't get it to work for individual cell background color.

```python
forEach({this.custom.data},
	forEach(it(),
		asMap(
			it()[0],
			asMap(
				"value", it()[1],
				"style", None
			)
		)
	)
)

```

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/a/3add19fe1b36e27693950a96860c2228d289d6e4.png)

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [August 5, 2024, 6:30pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/136 "2024-08-05T18:30:23Z")

</div>

Try setting style to be `asMap()` instead of `None`. That will make it behave as an object in the perspective component data. You can then also provide some default object members as well, such as `classes`.

Edit: Wait, are you trying to merge 2 maps?

---

<div class="post-metadata">

**Author:** ![dkhayes117](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dkhayes117/32/68344_2.png) [@dkhayes117](https://forum.inductiveautomation.com/u/dkhayes117)\
**Post date:** [August 5, 2024, 6:33pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/137 "2024-08-05T18:33:12Z")

</div>

Yes, I was already doing that. I omitted that part to be a cleaner post. I think the problem is the extra `0` layer in the JSON.

As is renders like so

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/f/8f8254390cdc00b608967c770dc4ed7e4ccbfc2d.png)

original data:

```python
[
  {
    "serial": 4363915,
    "cover": "03-01D",
    "backs": "07-01A",
    "seats": "07-01A",
    "frame": "0M91-51",
    "style": "M9231351",
    "in_queue": 1722880635000,
    "scheduled": 1723003200000
  }
]

```

expected output

```python
[
  {
    "serial": {"value":4363915, "style":{"backgroundColor":"someColor"} },
    "cover": {"value":"03-01D", "style":{"backgroundColor":"someColor"} },
    ...
  }
]

```

---

<div class="post-metadata">

**Author:** ![Matthew.gaitan](https://avatars.discourse-cdn.com/v4/letter/m/59ef9b/32.png) [@Matthew.gaitan](https://forum.inductiveautomation.com/u/Matthew.gaitan)\
**Post date:** [August 5, 2024, 6:38pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/138 "2024-08-05T18:38:25Z")

</div>

Try wrapping your entire expression with `flatten()` to remove the double `[[]]`

---

<div class="post-metadata">

**Author:** ![dkhayes117](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/dkhayes117/32/68344_2.png) [@dkhayes117](https://forum.inductiveautomation.com/u/dkhayes117)\
**Post date:** [August 5, 2024, 6:42pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/139 "2024-08-05T18:42:17Z")

</div>

That is closer, but still not right. `cover`, `in_queue` should both be under 0, 1, 2, ...

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/3/1/311fc6a35794de51d679e1fa4e291912075af37d.png)

---

<div class="post-metadata">

**Author:** ![Matthew.gaitan](https://avatars.discourse-cdn.com/v4/letter/m/59ef9b/32.png) [@Matthew.gaitan](https://forum.inductiveautomation.com/u/Matthew.gaitan)\
**Post date:** [August 5, 2024, 6:50pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934/140 "2024-08-05T18:50:05Z")

</div>

oh, i see what you're trying to do. I haven't found a good way to do that yet and have so far resorted to creating my maps by hand instead of iterating through the columns ☹

[Previous page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=6)

[Next page](https://forum.inductiveautomation.com/t/automation-professionals-integration-toolkit-module/74934.md?page=8)
