Do's and Don'ts when developing First Project with Ignition?

I have one more tip to add that I didn't see anyone else mention.

There are a lot of great tips about following naming conventions, creating folder hierarchies, and others. Whenever dealing with naming, folders, conventions, or patterns, write down what you're using; document it so others can reference it. It can even be helpful to yourself if you set down a project and come back to it awhile later.

Whenever a convention exists, it's more important that the convention is followed, even if there's a better way to do it. This can also give you a place to begin iterating on your design strategies to make them better for each next new project you start.

7 Likes

Also I was just thinking.

TRY TO REUSE AS MUCH MATERIAL AS YOU CAN!!!

If you do a class in Scripting that you use to process http messages (for example) try to make it so in your next project you can simply copy and paste it.

Do your work thinking in your next project (as much as you can).

Abstract coding is really cool, and when you are doing projects regullary its nice to be able to save time using material from other projects.

In my team we have a folder with .zips that we can simply import to our projects to save us time (as http message proccessing).

Another example is using """global""" variables instead of hard coding.
Note that this is sometimes really complicated to do at first and could cause more errors that being helpful.

2 Likes

so in your next project you can simply copy and paste it.

"Copy and paste" is antithetical to my idea of code reuse. Better to house reusable code in a base parent project that your others inherit from.

1 Like

Ryan is right. When I mean "copy and paste" i didn't mean it literaly. I mean in a way of reusing it.

In our case we use import/export depending of our needs.

As much as I love making abstractions and modular stuff...
I'd like to warn people about the danger of making things too abstract/too broad.

It's okay to duplicate a view instead of trying to make it fit two similar roles.

Example: If you need a view that allows the operator to create an entity, and another one to edit entities... Even if they look really similar, it's okay to have 2 distinct views instead of trying to make it do both at the same time. It's usually easier, cleaner, and it will allow you to change the logic in one part without worrying about breaking the other.

11 Likes

You can drag drop tags from a browsable PLC to make tags. Don't make tags you don't use. Excessive tag creation can create a lot of overhead that will dog your system.

I've seen people drag/drop 200k tags when they're using maybe 10 of them. It's something you absolutely want to avoid doing.

3 Likes

The bad part about being able to easily drag and drop tags from the PLC is that people often discover that and immediately drag the whole PLC to the tag browser and realize too late into the project that it was a horrible idea.

2 Likes

Hello,
i am not clear about use of this spreadsheet import tool. can you please advise it's use and benefits briefly ? thanks

Many Ignition applications/deployments use many device connections with corresponding UDT instances, or with large collections of UDT instances, or with a mix of UDTs and arbitrary tags and tag folders. It is common for there to be repeating patterns for most of this, but with enough odd elements for Ignition's instance creation wizard to be unsuited. (Well, the wizard is awfully simplistic--I never use it.)

With this tool, you can create structures in an Excel workbook that describes all of the device connections, corresponding UDT instances, and arbitrary tag instances, that one might need for a complex application. Using intelligent construction of device properties, UDT parameters, arbitrary tag properties, and initial value overrides in repeating patterns.

The resource includes examples of various such hierarchical structures, both simple ones, and moderately complex ones.

In reality, this works for testing or one-off tags that don't fit into a UDT, but 99% or more of my tags are part of a UDT. This is really how a lot of stuff should be done so that global changes can be made easily to similar tags rather than having a lot of one-off tags from drag and drop.

1 Like

I use UDTs for things that are cookie cutter-esque (device control, etc) but I still often encounter 1 off stuff in systems.

I definitely prefer when I can use UDTs for my tags because it is a lot quicker to implement things.

Add Doc Strings for all your functions and classes.

def func1(parms):
     """
          Helpful descriptions to help others understand your code.
          Note: will show up in autofill details, so keep it concise.
     """
     return parms

This well help your fellow devs re-use your code and save them time figuring out your function.

You can also print out these doc strings like this:

print func1.__doc__

Also, you can use dir() to find out what members a function/class have.
This can help you understand what tools you have available to you and also discover new things/features you never knew.

You can use dir() and doc strings in conjunction in the script console to learn about libraries/scripts without needing to go look up the documentation online.
For example:

# New unknown script package I have never used before
import json

# Find out all members in this package
for item in dir(json):
	print item

Output

JSONDecoder
JSONEncoder
__all__
__author__
__builtins__
__doc__
__file__
__name__
__package__
__path__
__version__
_default_decoder
_default_encoder
decoder
dump
dumps
encoder
load
loads
scanner
>>> 

From here you can print out the doc string to get some info on this function!

print json.dump.__doc__
"""
Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
    ``.write()``-supporting file-like object).

    If ``skipkeys`` is true then ``dict`` keys that are not basic types
    (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
    will be skipped instead of raising a ``TypeError``.

    If ``ensure_ascii`` is true (the default), all non-ASCII characters in the
    output are escaped with ``\uXXXX`` sequences, and the result is a ``str``
    instance consisting of ASCII characters only.  If ``ensure_ascii`` is
    ``False``, some chunks written to ``fp`` may be ``unicode`` instances.
    This usually happens because the input contains unicode strings or the
    ``encoding`` parameter is used. Unless ``fp.write()`` explicitly
    understands ``unicode`` (as in ``codecs.getwriter``) this is likely to
    cause an error.

    If ``check_circular`` is false, then the circular reference check
    for container types will be skipped and a circular reference will
    result in an ``OverflowError`` (or worse).

    If ``allow_nan`` is false, then it will be a ``ValueError`` to
    serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
    in strict compliance of the JSON specification, instead of using the
    JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).

    If ``indent`` is a non-negative integer, then JSON array elements and
    object members will be pretty-printed with that indent level. An indent
    level of 0 will only insert newlines. ``None`` is the most compact
    representation.  Since the default item separator is ``', '``,  the
    output might include trailing whitespace when ``indent`` is specified.
    You can use ``separators=(',', ': ')`` to avoid this.

    If ``separators`` is an ``(item_separator, dict_separator)`` tuple
    then it will be used instead of the default ``(', ', ': ')`` separators.
    ``(',', ':')`` is the most compact JSON representation.

    ``encoding`` is the character encoding for str instances, default is UTF-8.

    ``default(obj)`` is a function that should return a serializable version
    of obj or raise TypeError. The default simply raises TypeError.

    If *sort_keys* is ``True`` (default: ``False``), then the output of
    dictionaries will be sorted by key.

    To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
    ``.default()`` method to serialize additional types), specify it with
    the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.
"""

As you can see, no googling required, but now I know exactly what it going on in this function.

9 Likes

Good point, easy introspection is a strong feature of python.

Concerning docstrings, I'd suggest reading this:

It's always good to know the conventions !

1 Like

Adding to what you said @Julian_Islic .

I normally do this good practice:

def func1(parms):
	"""Helpful descriptions to help others understand your code.
	Note: will show up in autofill details, so keep it concise.

	Args:
		parms (dict): Small note explaining the param if necessary.

	Returns:
		tuple: Small explanations of the return
		
     """
     return parms

And will show something like this when previewed:
image

You could have multiple parameters listed there with the corresponding type and description. I belive the available types are int, float, bool, str, tuple, list, dict and any.

Also I think it's possible to only show the type without the description, but I wasn't able to do it.

image

Make sure to make short and specifics descriptions. This is not the place to explain in detail how the functions works.

2 Likes

Sorry for being late.

No one mentioned how to organize CSS Style??
Isn't it one purpose of using a saved style is to be able to reuse to multiple components.

Really, what is the most effective way to organize style?

I see style organization that was overthought.
So fancy that style being use uniquely for that component.

Make use of binding copy/paste:
image
I didn't realize that feature existed for the longest time, and it has made my life so much easier since I discovered it.

5 Likes

Thank you for that! Very useful @Ryan_Deardorff

Don't delete properties in Perspective (and maybe also Vision?) without first removing any bindings on them, or the bindings will persist and cause the property to reappear.

That's as of version 8.1.36, anyway. Seems like something they could potentially fix...

4 Likes

Same goes when renaming properties that have bindings on them. I suspect what is happening is there is some subprocess that is actively updating the property value based on the binding. When that active subprocess goes to update the property's value and the property has since been deleted or renamed it writes the value using the old property name, recreating the property. Now I generally just disable the binding before deleting or renaming the property.

2 Likes