# Templated map transforms

**URL:** <https://forum.inductiveautomation.com/t/templated-map-transforms/67211>\
**Category:** Ignition\
**Created:** [December 5, 2022, 12:17am UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211 "2022-12-05T00:17:24Z")\
**Posts on this page:** 12\
**Page:** 1

<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:** [December 5, 2022, 12:17am UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/1 "2022-12-05T00:17:24Z")

</div>

I had a potentially terrible idea that solves a missing feature (there's a forum post with the idea somewhere but I can't find it). The idea is to add the ability to create saved transform templates, for example save map transforms that you use all the time.

I had an idea that I could work around this by instead using a script transform with library functions that use a dictionary lookup to contain the mappings. The most obvious use-case for me is in device symbol status and mode displays to change the colour of the device based on the status tag. Function examples are below.

These would be used on every device on a page, between 5-40 devices maybe, and 70-130 clients.  
I'm just wondering how terrible would this be on gateway performance and if it's a good idea.. I always try to minimise the number of script transforms I use as these have slight performance impacts. The gain here is development time, maintainability, and standardisation. But if it's at the cost of client performance, then I don't think it's viable

```auto
def deviceStatus(statusDesc):
	statusMap = {
		'Stopped': 'Devices/Status/Off',
		'Closed': 'Devices/Status/Off',
		
		'Running': 'Devices/Status/On',
		'Opened': 'Devices/Status/On',
		
		'Faulted': 'Devices/Status/Faulted-nonflashing',
		
		'Starting': 'Devices/Status/On-trans',
		'Opening': 'Devices/Status/On-trans',
		
		'Stopping': 'Devices/Status/Off-trans',
		'Closing': 'Devices/Status/Off-trans',
		
		'Paused': 'Devices/Status/Paused'
	}
	
	return statusMap.get(statusDesc, 'Devices/Status/Invalid')

def deviceMode(modeDesc):
	modeMap = {
		'Manual': 'Devices/Modes/Manual',
		'Auto': 'Devices/Modes/Auto',
		'Maintenance': 'Devices/Modes/Maintenance',
		'Off': 'Devices/Modes/Off'
	}
	
	return modeMap.get(modeDesc, 'Devices/Modes/Invalid')

```

---

<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:** [December 5, 2022, 12:57am UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/2 "2022-12-05T00:57:43Z")

</div>

Write a module to make these expression transforms. Way more efficient. Configure using static datasets. (Or just use the `lookup()` expression function.)

BTW, if you **do** use scripts, a constant dictionary should be a script module top level variable, not set up inside the function. If the above is your typical code style, that would significantly impact performance. In other words, it should be like this:

```auto
_deviceStatusMap = {
	'Stopped': 'Devices/Status/Off',
	'Closed': 'Devices/Status/Off',
	
	'Running': 'Devices/Status/On',
	'Opened': 'Devices/Status/On',
	
	'Faulted': 'Devices/Status/Faulted-nonflashing',
	
	'Starting': 'Devices/Status/On-trans',
	'Opening': 'Devices/Status/On-trans',
	
	'Stopping': 'Devices/Status/Off-trans',
	'Closing': 'Devices/Status/Off-trans',
	
	'Paused': 'Devices/Status/Paused'
}
def deviceStatus(statusDesc):
	return _deviceStatusMap.get(statusDesc, 'Devices/Status/Invalid')

_deviceModeMap = {
	'Manual': 'Devices/Modes/Manual',
	'Auto': 'Devices/Modes/Auto',
	'Maintenance': 'Devices/Modes/Maintenance',
	'Off': 'Devices/Modes/Off'
}
def deviceMode(modeDesc):
	return _deviceModeMap.get(modeDesc, 'Devices/Modes/Invalid')

```

---

<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:** [December 5, 2022, 1:36am UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/3 "2022-12-05T01:36:38Z")

</div>

> [@pturmel](#):
>
> constant dictionary should be a script module top level variable

Ah yes, I do normally do this!

> [@pturmel](#):
>
> Write a module to make these expression transforms

I guess this would be a good introduction to module writing! 😬 I've always wanted to ~~break things~~ write a module. But as always, I would want to then extend it. Like make an interface for the dev to add their own custom map definitions, and importing/exporting them, etc... which I _really_ don't have time for but would obviously find time for 😄

> [@pturmel](#):
>
> (Or just use the `lookup()` expression function.)

This wouldn't be the end of the world, but it's less simple than I'd like since it requires copying the path of the dataset tag. It might be the biggest bang for the buck though

---

<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:** [December 5, 2022, 1:27pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/4 "2022-12-05T13:27:12Z")

</div>

I put my dictionaries at the top but I always did it because I think it's neater code wise.

Is there a serious performance issue with sticking the dictionaries inside the functions like that (assuming this function is run at a high frequency)? Does it have to "make" the dictionary every time the function is called where as it only makes it once when the module is loaded if its a top level variable? What's going on?

---

<div class="post-metadata">

**Author:** ![pascal.fragnoud](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pascal.fragnoud/32/24728_2.png) [@pascal.fragnoud](https://forum.inductiveautomation.com/u/pascal.fragnoud)\
**Post date:** [December 5, 2022, 1:48pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/5 "2022-12-05T13:48:32Z")

</div>

Yes, it will build the dictionary every time.  
In basic cases like this one, variables are destroyed when their scope is left. Which, in this case, is when the function returns.

I'm kind of guessing that in the case of generators, functions keep their local variables alive.  
I'm picturing it like a `static` variable in C

---

<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:** [December 5, 2022, 1:53pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/6 "2022-12-05T13:53:40Z")

</div>

> [@bkarabinchak.psi](#):
>
> Is there a serious performance issue with sticking the dictionaries inside the functions like that

Yes.

It is even more important for computed constants--things that need a complex operation or function to compute, but don't change after that. Such should be initialized at the top level from a function return value.

```python
def _someComplexInitialization():
    # do something complicated
    return result

someConstant = _someComplexInitialization()

```

---

<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:** [December 5, 2022, 2:30pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/7 "2022-12-05T14:30:10Z")

</div>

> [@nminchin](#):
>
> Like make an interface for the dev to add their own custom map definitions, and importing/exporting them, etc... which I _really_ don't have time for but would obviously find time for 😄

What exactly are you envisioning here? Something like style classes?

I can see where you're going with a map transform, but what about the other transforms. How would this apply for (Format, Script, and Expression transforms?)

---

<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:** [December 5, 2022, 6:42pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/8 "2022-12-05T18:42:09Z")

</div>

> [@lrose](#):
>
> What exactly are you envisioning here? Something like style classes?

Not sure, but I guess so. Just somewhere to configure and save your map transforms. Ideally you'd then be able to select them in the map transform area within a binding.

You're right, it only really makes sense for the map transform and none of the others

---

<div class="post-metadata">

**Author:** ![Steve\_Laubach](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/steve_laubach/32/60900_2.png) [@Steve\_Laubach](https://forum.inductiveautomation.com/u/Steve_Laubach)\
**Post date:** [December 5, 2022, 7:35pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/9 "2022-12-05T19:35:34Z")

</div>

I agree that it would be nice if there was something like a style class for map transforms.

You can use an embedded view to lock in a static map transforms and pass in the binding that the map transform is applied to. That's probably one of the more elegant ways to do at this time.

---

<div class="post-metadata">

**Author:** ![amy.sudibyo](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amy.sudibyo/32/38659_2.png) [@amy.sudibyo](https://forum.inductiveautomation.com/u/amy.sudibyo)\
**Post date:** [December 5, 2022, 10:02pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/10 "2022-12-05T22:02:48Z")

</div>

I can definitely see a use case for this. I find myself regularly using the same map transforms throughout a project to apply enabled/disabled style classes to components.

---

<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:** [December 6, 2022, 1:56am UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/11 "2022-12-06T01:56:27Z")

</div>

> [@Steve\_Laubach](#):
>
> You can use an embedded view

Every embedded view layer increases load time though, so this would be my least preferred option, not that I haven't done this in the past though. But these days I would avoid this option. I've gone with Phil's suggestion to use tag datasets and the lookup expression function which isnt too bad and offers excellent performance. Would still prefer templated transforms though!

---

<div class="post-metadata">

**Author:** ![Steve\_Laubach](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/steve_laubach/32/60900_2.png) [@Steve\_Laubach](https://forum.inductiveautomation.com/u/Steve_Laubach)\
**Post date:** [December 7, 2022, 1:21pm UTC](https://forum.inductiveautomation.com/t/templated-map-transforms/67211/12 "2022-12-07T13:21:47Z")

</div>

Oh yeah. I really like the idea of transforms.
