ESI Coding Standards

The ESI libraries are now incorporated into the internal Lua libraries of the system and are updated as part of regular product development. The esi git repository is still available but will not updated in future. Requests for improvements can still be made by ESI community members there or by contacting our support team.

ESI coding standards have to be kept without any deviation by any individual and team contributing to the ESI standard. Code which does not adhere to the actual standard will be rejected from entering the ESI GitHub repository. Any suggestions to improve the coding standards are welcome and shall be posted in the repositories’ discussion board Issues.

Important Notice: Nothing specified in this document shall ever be treated as a nice idea, in the opposite, every statement in this document is absolutely binding for every ESI developer and contributor. Code which violates the specification will be tolerated for a short period of time, and banned from the repository in case authors refuse necessary rework.

Version History

Version Date Description

0.1.1

2018-06-05

Introduced versioning, max code line length convention added

0.1.0

2018-03-03

First published

Code Editor

All ESI code needs to be edited and checked for errors using Microsoft Visual Studio Code, furthermore referenced as VS Code, and finally to be tested on an inmation system. VS Code can be publicly downloaded for various platforms and is free-of-charge. Microsoft updates VS Code frequently with improvements and new features. Make sure you have the latest release. The editor will notify you when a new version is available. Since the in-built inmation code editor in Data Studio doesn’t have as much code inspection as VS Code, this editor should only be used to paste code into the system to test the functionality for robustness.

Visual Studio Code can be downloaded here.

Extensions

VS Code can be more powerful by using extensions. The first two extensions are really important which makes VS Code recognize Lua code and checks it for coding errors. The ESI standard requires the following extensions to be installed:

extension version purpose

Lua

0.0.9

Lua language support for Visual Studio Code

Luacheck

1.0.0

Static error checker for Lua

Lua Debugger

0.0.23

Visual Studio Code debugger extension for Ravi and Lua 5.3

ESI to Markdown

0.1.5

Create Markdown documentation files from Lua files

The versions are indicative and may also be newer. After installation make sure every extension is enabled.

Integrity Check

Extension Luacheck makes use of luacheck which is a static analyzer and a linter for Lua. Luacheck detects various issues such as usage of undefined global variables, unused variables and values, accessing uninitialized variables, unreachable code and more.

Before a piece of Lua code may be pasted into an inmation system, it must be checked for static errors. Issues will be shown in the 'Problems' panel. All issues needs to be solved before the code may be pasted into the inmation system. The following screenshots show some chunks of Lua code not qualifying for the transfer into inmation:

image

Let’s first remove trailing whitespace:

image

'exampleLib' was declared globally. Solve this by putting 'local' before the variable:

image

When using the colon ':' notation the first 'invisible' argument is 'self'. Since self is not used, this can be fixed by changing the colon to a dot and place an underscore as first argument: Note: The function invocation will still be exampleLib:multiply(10, 20)

image

Visual Studio Code Settings

in order to keep the source code clean across all contributors, the default tab size of 4 shall be the binding standard. Please check your editor preferences for

"editor.tabSize": 4,

Maximum line length

A code line shall never exceed 120 characters. Break longer instructions like this:

local suc,res=pcall(
    function() return inmation.setvalue(self.path..".StateManagement.StateTable",self.tstate) end
)

and use multi-line strings for larger strings:

local sql=[[--
SELECT [a_column], [and], [another_column]
FROM [this_database].[and_schema].[and_finally_the_tablename]
WHERE [a_column]='unlikely to ever by like that, but it is a sample'
--]]
query(con,sql)

ESI Conventions

There are two types of libraries. The first type is supplying functions and classes of any purpose. The second type encapsulates complete use cases enabling declarative high level scripts. We call the first type common ESI libraries and the second type use case ESI libraries.

First we introduce conventions valid for both types.

  • All functions satisfying the ESI conventions are to be CAPITALIZED, when such a function shall be exposed to outside callers.

  • The module must be named "esi-something-lib.lua" when it is stored in the repository. The something part shall describe the purpose of the library clear and crisp.

  • Each library has to start with a INFO function, which returns a set of information in a Lua table. The minimal content should be:

function lib.INFO(_)
    return {
        version = {
            major = 0,
            minor = 1,
            revision = 1
        },
        contacts = {
            {
                name = "Timo Klingenmeier",
                email = "timo.klingenmeier@inmation.com"
            },
            {
                name = "Marc van de Langenberg",
                email = "marc.vandelangenberg@inmation.com"
            }
        },
        library = {
            -- Filename is always modulename plus "-lib.lua" and the modulename must be used for the ScriptLibrary.LuaModuleName property.
            modulename = "esi-example"
        }
    }
end

Please note that the INFO function above already satisfies another ESI Convention (all public functions have to be capitalized and called with a colon), such as

local i=LIB:INFO()

The invisible self parameter which is handed over by such a call is not accessed inside the INFO function body, and as such it is marked with underscore in the function declaration.

The version numbers

major should only be set to one '1' in case the library development has been finished and the library is in productive use (not in testing). It should be increased above '1', in case a library has undergone major extensions or rework and is in production again.

minor should be increased with every code change, which brings new features / functions / classes and the like.

revision needs to be changed with every commit,e.g. after bug fixing.

Breaking changes

Breaking changes may never be introduced without changing the major version number part.

The module name

The ESI steering team has decided to "walk away" from any dotted, capitalized notation for ESI libraries (and any inmation customization libraries in general). Instead, the notation shall follow the one which is used for the files in this GIT repository, the dash-separated all lower case naming convention. Each ESI library has to start with esi- respectively.

The module name mentioned in the table field modulename must be used when this library is loaded into a production system.

If the author has specified 'esi-kpi-oee' to be the modulename, it must appear like this in inmation:

image

…​and this modulename is also the only "name version" of this library which shall ever be required:

local ESIOEE = require('esi-kpi-oee')

Commenting

All comments shall be written in English language. All script authors are encouraged to make use of code comments whenever a certain program logic is not completely obvious.

Keywords in comments

ESI defines three keywords for commenting. The keyword must be the first word in the comment, followed by a colon, e.g.

-- note: The following logic needs to be checked for a nil value in variable x
keyword meaning

bug

Someone has identified a bug which needs to be fixed

note

An important statement about the code below

todo

The author is aware of a required improvement but could not yet implement it

The first code line

of each ESI library shall always be the standard modulename (followed by an optional "Library") as a comment only. This will allow DataStudio users to visually confirm the match between the module name foreseen by the author and the module name which was given by the system administrator when assigned to an inmation Script Library property compound. See the screen shot for the modulename sample above.

-- esi-kpi-oee Library

or

-- esi-kpi-oee

The Library Scope

inmation Lua libraries require a scope which needs to be returned at the end of the library. The library scope must always be

local

Important Notice: It was recently decided to steer away from any specific names for the library scope, but to use just lib always. An example for a valid ESI library scope is shown below:

-- esi-barebone
local lib={}

-- note: the required INFO function has been omitted for readability

function lib:UPPER(x)
    return tostring(x):upper()
end

return lib

Calling Conventions

In the above example two things are notable, which are fixed requirements for ESI compatibility:

  • All ESI functions must be implemented so they are called using the colon lib:FUNC() notation. Whether the implicit self is required in the function body or not is not important, it is a convention.

  • If the function is to be exposed by the library, its name must be capitalized, if it is an internal (helper) function, its name must be lowercase with a leading underscore.

  • In cases where the intrinsic self parameter is not used, we need to do a classic dot-based declaration in order to not raise linter problems

-- this would not please the linter, because the invisible self parameter is not used in the function body
function lib:_makesqldate(posix)
    assert('number'==type(posix))
    return inmation.gettime(posix):sub(1,19):gsub("T","")
end

-- this is the 'work-around' to please the linter
function lib._makesqldate(_,posix)
    assert('number'==type(posix))
    return inmation.gettime(posix):sub(1,19):gsub("T","")
end

Stateful Libraries

Stateful libraries manage their own variables over the uninterrupted runtime being invoked by an inmation script object (such as an ActionItem or a periodic GenericItem with the Lua generation option set). The library can declare any variables:

lib.calls=0
lib.data={}

and by the general access to self using the colon declaration and calling convention each function in the library can access it easily:

function lib:_somefunction()
    self.calls=self.calls+1
    table.insert(self.data,{some="Some",other="Other"}
end

Error Handling

Error handling is different for common and declarative ESI libraries. The declarative ESI requirements for error handling will be described in this document. This is currently work-in-progress.

For common ESI libraries, the following rules apply:

  • The usage of error and assert need to be controlled, as they will break the execution in an inmation environment, which is very unlikely to be wanted in a production environment

  • Instead such functions should be wrapped so that the behavior can be changed in a single location. See the code example below.

local lib={}
lib.debug=true
function lib:_error(...)
    if self.debug then
        error(...)
    else
        -- other error handling in production
    end
end
function lib:_assert(...)
    if self.debug then
        assert(...)
    else
        -- other error handling in production
    end
end

-- in the rest of the library body, the wrappers are to be used *exclusively*
function lib:CALL(x)
    self:_assert("number"==type(x))
    self:_assert(0~=x)
    self:_error("Try the error")
end
return lib

Documentation

Each library has to be documented using Markdown. There is a Visual Studio Code extension ESI to Markdown which will create a markdown file directly from the esi lua file.

Public Interface

By the ESI specification, the public interface of a library are all functions with CAPITALIZED names. The author of a particular library has to supply a call documentation (preferably with examples for functions with flexible arguments) for each of the functions exposed in the public interface.

Non-public functions

Each library may contain any number of internal helper functions, which are not supposed to be used directly outside of the library, neither be documented in the .md file associated with the libary. By convention, each private function must begin with an underscore and have a name which is exclusively lower case characters.

-- it is a good idea to have a brief description in the source code
-- because this function is not going to be documented in the markdown
function lib:_my_helper(arg)
-- ... do whatever
end

Screenshots

Screenshots shall be supplied in all cases, where the usage of a specific function in the inmation environment helps to understand the usage of a particular function in the target environment.

Drawings

In case the author decides to add drawings to the documentation (which is encouraged for highly complex libraries), the same applies as for Screenshots.