# Custom Function Implementation

**URL:** <https://forum.inductiveautomation.com/t/custom-function-implementation/14318>\
**Category:** Module Development\
**Tags:** sdk, ignition79\
**Created:** [May 17, 2017, 8:47am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318 "2017-05-17T08:47:54Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 17, 2017, 8:47am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/1 "2017-05-17T08:47:54Z")

</div>

Hello everyone,

My company want me to implement a few custom expression functions to Ignition as shown below:

 ![](https://us1.discourse-cdn.com/inductiveautomation/original/2X/0/0d29c36e6aee816ff405de48cb697e7ca2b61e3d.png)).

However, I am new to Ignition and unfortunately, I can’t find out how to add the custom expression functions with the help of the documentation. As far as I know, I have to first extend the _AbstractGatewayModelHook_.

After that, the problem(s) starts:

1. How to get access to the “expression” functions drop down list?

2. How to add the “custom” functions within the existing function drop down list?

3. How can I “import” the custom code to the tool? In mean in the documentation it is described, that the gateway can load a .modl file, but where do I find this gateway within the tool?

Thank you very much in advance for help.

---

<div class="post-metadata">

**Author:** ![KathyApplebaum](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kathyapplebaum/32/7261_2.png) [@KathyApplebaum](https://forum.inductiveautomation.com/u/KathyApplebaum)\
**Post date:** [May 17, 2017, 2:12pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/2 "2017-05-17T14:12:37Z")

</div>

Your best bet is to start by becoming familiar with Ignition, because you’ll need to understand the difference between the Gateway, Designer, and Client before you can start writing custom modules. The free courses at [Inductive University](http://inductiveuniversity.com/) are a great place to start.

Once you’re familiar with Ignition, work through compiling and installing one of the example SDK modules, so you can see how to build, sign, and load a module without worrying about adding new code. Until you’ve mastered those steps, it’s a bit early to worry about #1 and #2 in your question.

---

<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 18, 2017, 1:38am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/3 "2017-05-18T01:38:50Z")

</div>

Just to add some clarification to Kathy’s comment: Writing custom expression functions requires writing and compiling _ **Java** _ code and distributing it in a .modl file. Not only can your _not_ do this with jython, the expression function system is almost totally separate from the jython scripting system.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 18, 2017, 7:37am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/4 "2017-05-18T07:37:01Z")

</div>

Hello Kathy and pturmel.

Thank you very much for your answers. I will take a look at the video materials you linked, but I am not sure if they help me with my specific problem. pturmel, I am aware, that you need Java development knowledge, which is not the problem. In addition, I got a sample code from one of the inductive automation Sales Engineers.

Gateway:

> public class GatewayHook extends AbstractGatewayModuleHook {

> ```
> private final Logger logger = LoggerFactory.getLogger(getClass());
> private GatewayContext gatewayContext;
> 
> ```

> ```
> @Override
> public void setup(GatewayContext gatewayContext) {
> 
> ```

> ```
> this.gatewayContext = gatewayContext;
> }
> 
> ```

> ```
> @Override
> public void startup(LicenseState licenseState) {
> 
> ```

> ```
> }
> 
> ```

> ```
> @Override
> public void shutdown() {
> 
> ```

> ```
> }
> 
> ```

> ```
> @Override
> public void configureFunctionFactory(ExpressionFunctionManager factory) {
> factory.addFunction(ConvertBase.FUNC_NAME, ConvertBase.CATEGORY, new ConvertBase(gatewayContext));
> }
> 
> ```
> 
> }

Convert Base:

> public class ConvertBase implements Function {

> ```
> public static String FUNC_NAME = "ConvertBase";
> public static String CATEGORY = "FSQL_Tools";
> 
> ```

> ```
> private GatewayContext context;
> 
> ```

> ```
> public ConvertBase(GatewayContext context) {
> this.context = context;
> }
> 
> ```

> ```
> public static void main(String[] args) {
> System.out.println(CATEGORY);
> System.out.println();
> 
> ```
> 
> }
> 
> ```
> @Override
> public QualifiedValue execute(Expression[] expressions) throws ExpressionException {
> QualifiedValue from = expressions[0].execute();
> QualifiedValue to = expressions[1].execute();
> QualifiedValue numberToConvert = expressions[2].execute();
> 
> ```

> ```
> Quality quality = DataQuality.worstOf(from.getQuality(), to.getQuality());
> quality = DataQuality.worstOf(quality, numberToConvert.getQuality());
> 
> ```

> ```
> Integer inputInt = Integer.valueOf(numberToConvert.getValue().toString(), (Integer) from.getValue());
> String output = Integer.toString(inputInt, (Integer) to.getValue());
> 
> ```

> ```
> return new BasicQualifiedValue(output, quality);
> }
> 
> ```

> ```
> @Override
> public void initArgs(Expression[] args) {
> if (!validateNumArgs(args.length)) {
> throw new RuntimeException("Function '" + FUNC_NAME + "(" + getArgDocString()
> + ")' does not work with " + args.length + " arguments.");
> } else {
> for (int i = 0; i < args.length; i++) {
> if (!validateArgType(i, args[i].getType())) {
> throw new RuntimeException("Type mismatch: Argument " + i + " for function '" + FUNC_NAME
> + "(" + getArgDocString() + ")' is an invalid type.");
> }
> }
> }
> }
> 
> ```

> ```
> protected boolean validateNumArgs(int num) {
> return num == 3;
> }
> 
> ```

> ```
> protected boolean validateArgType(int argNum, Class<?> type) {
> boolean ret = false;
> 
> ```

> ```
> switch (argNum) {
> case 0:
> ret = type == Integer.class;
> break;
> case 1:
> ret = type == Integer.class;
> break;
> case 2:
> ret = type == String.class;
> break;
> }
> 
> ```

> ```
> return ret;
> }
> 
> ```

> ```
> @Override
> public Class<?> getType() {
> return String.class;
> }
> 
> ```

> ```
> @Override
> public String getArgDocString() {
> return "from, to, numberToConvert";
> }
> 
> ```

> ```
> @Override
> public Function copy() {
> return this;
> }
> 
> ```

> ```
> @Override
> public void connect(BaseContext baseContext, InteractionListener interactionListener) {
> 
> ```

> ```
> }
> 
> ```

> ```
> @Override
> public void disconnect() {
> 
> ```

> ```
> }
> 
> ```

> ```
> @Override
> public void startup() {
> 
> ```

> ```
> }
> 
> ```

> ```
> @Override
> public void shutdown() {
> 
> ```

> ```
> }
> 
> ```
> 
> }

However, I wonder how to test if the code works as he told me it would. This leads back to my question, How to transform his code (which I have in Eclipse) to this curious "modl" file and tell Ignition to apply the changes?

Thank you very much for your help.

---

<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 18, 2017, 12:13pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/5 "2017-05-18T12:13:03Z")

</div>

> [@alain.pfeiffer](#):
>
> This leads back to my question, How to transform his code (which I have in Eclipse) to this curious "modl" file and tell Ignition to apply the changes?

As Kathy said, you need to use the SDK, linked from[the online docs](https://docs.inductiveautomation.com/display/DOC79/SDK+Documentation). You must set up maven in your dev environment and create a module properties file. You may also need to create code for the client and designer scopes, if you want your new function to work there as well. You will need a java code signing certificate, or a self-signed one. When you finally get a .modl file built, you use Ignition's web gateway interface to install it into your running server.  
I can't possibly list all the steps and nuances here -- that's why IA has a [git repo with a bunch of examples](https://github.com/inductiveautomation/ignition-sdk-examples) for various scenarios. (Linked in the SDK Docs.) The code snippet you were given presumes you've accessed that and understood how a module is structured.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 18, 2017, 12:50pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/6 "2017-05-18T12:50:41Z")

</div>

Hi Phil,

You are totally right! However, I read that manuel. At page 13 it is "described" how to form an .modl in the following way:

> Compile and Deploy  
> Now all of your projects should be in Eclipse and it will compile (or "build") all of them. You'll notice  
> that one of the projects is called "Build". This project has all of the Ant scripts that are used to  
> compile and assemble the Java code into an Ignition \*.modl file. Each project has its own build file.  
> For example, the component example projects have a build file called build-componentexample.  
> xml. Double click on this Ant file to open it in Eclipse's Ant editor. (If it opens in a raw  
> XML editor, close that, and then right click on it and choose Open With \> Ant Editor). You'll see in  
> Eclipse's Outline view each of the Ant script's Targets. These are what you'll use to compile your  
> module. You can right-click on the "build" target and choose Run As \> Ant Build to build that  
> module file. If you have a Developer Mode Gateway running locally, you can run the "deploy" target  
> to push your newly built module to your Gateway.  
> That's it! You're up and running. You've compiled and deployed a module using the SDK. Now you  
> can write your own module or adapt one of the examples to create new functionality for Ignition.

However, there are several problems:

1. Where are theses "Ant scripts" located in the "build" project? I have an ExpressionFunctions-build project, but don't see any of theses scripts.

2. I don't see any "...build.xml" files in any of my projects. I have some Expression-Functions-build.iml files!

3. The rest of the text I don't need to mention, because step 1. and 2. have to be solved first.

Thank you for help.

---

<div class="post-metadata">

**Author:** ![fourmajor](https://avatars.discourse-cdn.com/v4/letter/f/898d66/32.png) [@fourmajor](https://forum.inductiveautomation.com/u/fourmajor)\
**Post date:** [May 18, 2017, 2:33pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/7 "2017-05-18T14:33:49Z")

</div>

I wrote [a blog post](http://blog.perfectabstractions.com/2017/03/07/getting-started-with-ignition-module-development/) that may be helpful in getting a basic module built and installed.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 19, 2017, 10:18am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/8 "2017-05-19T10:18:21Z")

</div>

Thank your fourmajor for the blog post. It was very helpful 🙂 I was able to install the above module. However, I can’t find the “new” function. Does anyone have an idea what went wrong? The logs does not tell that any errors occurred.

Log: Starting up module ‘com.paulwurth.expressionfunctions’ v1.0.0 (b0)…

In addition, this picture shows, that it was successfully installed:

 ![](https://us1.discourse-cdn.com/inductiveautomation/original/2X/0/0acfe76d18c8fdb889dd58dd6e24dd9967248a9f.png)

Thank you

---

<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 19, 2017, 11:44am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/9 "2017-05-19T11:44:28Z")

</div>

If you only did a gateway hook, you’ll only have the new function in tag expressions, and you’ll have to enter it blind, as the designer won’t know about it.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 19, 2017, 11:48am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/10 "2017-05-19T11:48:01Z")

</div>

Hi Phil,

Thank you for your answer but I don’t know what you mean by that? The code has a gateway hook which implements the abstract gateway and the convertbase that implements a function.

What else do I need to see the function then appearing somewhere?

Thank you

---

<div class="post-metadata">

**Author:** ![KathyApplebaum](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kathyapplebaum/32/7261_2.png) [@KathyApplebaum](https://forum.inductiveautomation.com/u/KathyApplebaum)\
**Post date:** [May 19, 2017, 2:08pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/11 "2017-05-19T14:08:36Z")

</div>

If you want to see your function in the Designer, you need to also have a designer hook that implements your function.

Again, I’ll suggest that you watch some of the free videos at Inductive University, especially the [gateway](https://inductiveuniversity.com/course/ignition-gateway) and the [designer](https://inductiveuniversity.com/course/ignition-designer) videos so that you know the difference between them.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 22, 2017, 9:32am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/12 "2017-05-22T09:32:21Z")

</div>

Hello Kathy,

Thank you very much for your answer. However, I don’t understand what you mean by an “designer hook” that implements the function. Where do I have to add that class in my project structure? Can you please clarify that up, or provide me an example of such a designer hook? Here is the full project example I have until now: [PaulWurthExpressionFunctions.zip](https://us1.discourse-cdn.com/inductiveautomation/original/2X/a/aa32ee49d46aeab1bc6ad0d94090c622641608fd.zip) (77.5 KB)

P.s. I watched all the video materials already, and they don’t help a single bit because they do not have anything to do with developing your own module. Never the less, thank you for that.

---

<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 22, 2017, 12:08pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/13 "2017-05-22T12:08:32Z")

</div>

Consider reviewing the structure of the [scripting-rpc example module](https://github.com/inductiveautomation/ignition-sdk-examples/tree/master/scripting-rpc-example). It has all three scopes and the typical project structure for producing it.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 22, 2017, 1:28pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/14 "2017-05-22T13:28:27Z")

</div>

Hi Phil,

Thank you very much for the answer. However, the example is not even working because there is a missing jar… eclipse/milo/sdk-client/0.1.0-SNAPSHOT/sdk-client-0.1.1-SNAPSHOT.jar.

I found this [post](http://forum.inductiveautomation.com/t/missing-dependency-in-sdk-example-modules/12163), but my pom file does not look like JMcguigan pom and hence I can’t fix the issue in the same way. So how should I fix the issue to test the example so that I may understand how I can make my example run?

---

<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 22, 2017, 2:17pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/15 "2017-05-22T14:17:25Z")

</div>

As you might have noticed from my comment in that thread, I can’t help you with maven or the proper resolution of dependencies in its pom files. (I still use ant for my modules.) My point was that deploying a custom function generally requires a multi-scope project (Gateway, Designer, Client) and the scripting-rpc project shows how to divide up and organize your code.

---

<div class="post-metadata">

**Author:** ![PerryAJ](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/perryaj/32/3281_2.png) [@PerryAJ](https://forum.inductiveautomation.com/u/PerryAJ)\
**Post date:** [May 22, 2017, 4:25pm UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/16 "2017-05-22T16:25:53Z")

</div>

It sounds like you might be using an older version of the SDK, so you may benefit from updating the versions in your pom.xml to `7.8.5`, or adding the the repo posted in the post you reference to your `<respositories>` list.

The problem you are seeing results from the use of an earlier dependency on the milo library, which was published to the sonatype snapshots, and not yet maven central (as it was pre-release at that point).

In terms of the scope issue, Kathy and Phil are leading you down the right path in figuring out how to add your expressions to the designer. Your expression should already run on gateway when saved and executed, but the designer knows nothing about them. In order to make the designer aware, you also need to add a designer-scoped subproject to your module as the example Phil linked demonstrates.

---

<div class="post-metadata">

**Author:** ![alain.pfeiffer](https://avatars.discourse-cdn.com/v4/letter/a/fbc32d/32.png) [@alain.pfeiffer](https://forum.inductiveautomation.com/u/alain.pfeiffer)\
**Post date:** [May 24, 2017, 6:52am UTC](https://forum.inductiveautomation.com/t/custom-function-implementation/14318/17 "2017-05-24T06:52:06Z")

</div>

Thank you Perry for your answer,

1. You are right, I had to use 7.8.5 instead of the predefined 7.9.0-SNAPSHOT. However, to be honest, examples should be correct from the beginning on and this should be fixed so that no one else encounter this kind of problem again.

2. I am aware that they want to lead me the right way. However, it would be much easier and faster if the example function would at least show up in the designer so that I could finally start with the implementations of the custom functions. Because all this steps leads to addition problems. Now, I think I have the “structure” as it should be but I still can’t see the function within ignition. Any other ideas or is still something wrong?[PaulWurthExpressionFunctions (new).zip](https://us1.discourse-cdn.com/inductiveautomation/original/2X/0/020546e72ee2f2f22848a03040a47eacbfd95475.zip) (78.8 KB)

Thank you for help.
