Function and CustomScript Mapping

Function Mapping

This mapping type is called 'Function Mapping' and is very similar to the 'Custom Script' mapping. But instead of providing lua code directly, this mappling allows to call a function from a library instead.

In order to use 'Function Mapping', the Mapping Type needs to be set to "FunctionMapping" which will change the available Options below.

Mapping Type - Custom
Figure 1. Mapping Type - Function Mapping

The First paramter is the library name to use for this mapping. This library must be directly inherited from "esi-msi-function" ("EXTEND") in order to prevent missuse of this configuration.

The second paramter is the name of the function for processing of incomming data (similar to Input Script). And the third paramter is a JSON string, which is passed as additional lua table to the input function. The these two paramters are only used if the Communication mode is Send or Bidirection.

The fourth parameter is the name of the function for processing of outgoing data (similar to Output Script). And the fifth paramter is a JSON string, which is passed as additional lua table to the output function.

The requirement to inherit from esi-msi-function prevent an accidential configuration and triggering of random library function and allows to provide some interface documentation together with esi-msi-function library documentation.

The two additional paramter strings (JSON format) allow for a flexible configuration of potentially generic functions. Both function have to be part of the same library.

A simple example of an library with input and output function:

-- MSI_FUNC_EXAMPLE
local JSON = require('rapidjson')
local MSI = require("esi-msi-function")

local funcT = MSI:__EXTEND__("MSI_FUNC_EXAMPLE",{})

function funcT:INPUT(content, optTable, mesgId, mapTable)
     -- mapTable allows acces to the central mapping but is not used here
     local dataInPath = optTable.base.."/integrationTest/MProF/DataIn"
     content.msgId = msgId    -- there is no msgId field in the content, so no name clash
     local jsonData = JSON.encode(content)
     local res = syslib.setvalue(dataInPath, jsonData)
     return res
end

function funcT:OUTPUT(optTable, mapTable)
     -- mapTable allows acces to the central mapping but is not used here
     local dataOutPath = optTable.base.."/integrationTest/MProF/DataOut"
     local dataAbortPath = optTable.base.."/integrationTest/MProF/DataAbort"
     local jsonData = syslib.getvalue(dataOutPath)
     local jsonAbort = syslib.getvalue(dataAbortPath)
     if jsonAbort then
          local abort = JSON.decode(jsonAbort)
          syslib.setvalue(dataAbortPath, "")
          return nil, abort, abort.msgId
     elseif jsonData then
          local data = JSON.decode(jsonData)
          syslib.setvalue(dataOutPath, "")
          return data, nil, data.msgId
     else
          return
     end
end
return funcT

The output table for data must be of the form:

{parameter=[{ name=ObjectName1, value=Value1},{name= ObjectName2, value=Value2}]}

Where "ObjectName1" and "ObjectName2" must match the object names defined in the Tag List table. Value1 and Value2 are the values assigned to these object names in the response. Be aware that the content value1 and value2 must match the type defined in the Tag List table.

An example response would be:

{"parameter":[ {"name":"RecipeName","value":"Recipe1"},
               {"name":"OrderNumber","value":"orno_0101"},
               {"name":"MaterialName","value":"matname_0101"},
               {"name":"BatchID_basic","value":"Batch_b_01"},
               {"name":"MaterialNumber","value":"matno_0101"}
          ]}

The output table for abort must be of the form:

{parameter= {{ name=Qualifier1, value=Value1},{name= Qualifier2, value=Value2}},
    errors= {{ code= "101",  text = "something went wrong..." }} }

Where the qualifiers are the MES qualifiers needed to assigne the abort message to the correct EBR in the MES. The errors-code and errors-test are code and text of the error leading to the abort.

In addition to the content and the abortContent, a message Id can be given, which allows the MSI interface to process several pending messages for the same 'Message Configuration' in parallel. If the message Id is not given, a outgoing message will remove all pending messages for that 'Message Configuration' from the list of pending messages. If the msgId is given, only the message with matching Id is removed.

All values are automatically converted to string during XML encoding, which automatically complies with the interface definition of MSI Interface. However, if problems should occur during this string conversion, the values can also be given as strings in the corresponding format.

E.g. Double being represented as "3.41" and bool being "true" or "false". In addition to the parameter section of the message, all other information needed to generate the message is taken from the configuration above. This includes qualifier information, MessageID etc.

Custom Script

The next mapping type is called 'custom script' and this mapping is completely done by custom scripts.

In order to use custom mapping, the Mapping Type needs to be set to "Custom" which will change the available Options below.

Mapping Type - Custom
Figure 2. Mapping Type - Custom

There are two scripts, the first one being called the Input Script. This script shall return a function, which is executed whenever a message is received. The input parameter of this function is the content of the message in form of a Lua table. This script can return true or false.

A simple example of an input script like this:

local dataInPath = "/System/Core/integrationTest/MProC/DataIn"
local JSON = require('rapidjson')
local fun = function(content, msgId, mapTable)
     -- mapTable allows acces to the central mapping but is not used here
     content.msgId = msgId  -- there is no msgId field in the content, so no name clash
     local jsonData = JSON.encode(content)
     local res = syslib.setvalue(dataInPath, jsonData)
     return res
end
return fun

The second script is the Output Script. This script is executed every second to see if there is data available for sending. If no message shall be sent, this script needs to return nil. Otherwise it has to return the data matching the message configuration. An example of a simple output script, only returning data read from an IO-Item is:

local dataOutPath = "/System/Core/integrationTest/MProC/DataOut"
local dataAbortPath = "/System/Core/integrationTest/MProC/DataAbort"
local JSON = require('rapidjson')
local fun = function(mapTable)
     -- mapTable allows acces to the central mapping but is not used here
     local jsonAbort = syslib.getvalue(dataAbortPath)
     local jsonData = syslib.getvalue(dataOutPath)
     if jsonAbort then
          local abort = JSON.decode(jsonAbort)
          syslib.setvalue(dataAbortPath, "")
          return nil, abort, abort.msgId
     elseif jsonData then
          local data = JSON.decode(jsonData)
          syslib.setvalue(dataOutPath, "")
          return data, nil, data.msgId
     else
          return
     end
end

The output data table must be of the form:

{parameter= { { name=ObjectName1, value=Value1},{name= ObjectName2, value=Value2}} }

Where "ObjectName1" and "ObjectName2" must match the object names defined in the Tag List table. Value1 and Value2 are the values assigned to these object names in the response. Be aware that the content value1 and value2 must match the type defined in the Tag List table.

An example response would be:

{parameter={ {name=RecipeName",    value="Recipe1"},
             {name=OrderNumber",   value="orno_0101"},
             {name=MaterialName",  value="matname_0101"},
             {name="BatchID_basic",value="Batch_b_01"},
             {name=MaterialNumber",value="matno_0101"}
}}

The output table for abort must be of the form:

{parameter= {{ name=Qualifier1, value=Value1},{name= Qualifier2, value=Value2}},
    errors= {{ code= "101",  text = "something went wrong..." }} }

Where the qualifiers are the MES qualifiers needed to assigne the abort message to the correct EBR in the MES. The errors-code and errors-test are code and text of the error leading to the abort.

In addition to the content and the abortContent, a message Id can be given, which allows the MSI interface to process several pending messages for the same 'Message Configuration' in parallel. If the message Id is not given, a outgoing message will remove all pending messages for that 'Message Configuration' from the list of pending messages. If the msgId is given, only the message with matching Id is removed.

All values are automatically converted to string during XML encoding, which automatically complies with the interface definition of MSI Interface. However, if problems should occur during this string conversion, the values can also be given as strings in the corresponding format.

E.g. Double being represented as "3.41" and bool being "true" or "false". In addition to the parameter section of the message, all other information needed to generate the message is taken from the configuration above. This includes qualifier information, MessageID etc.

Using Central Mapping

The actual use case where CustomScript and Function mapping are used, might not be tag base, so that a 'Central Mapping' is not needed. But there might also be use cases which do rely on the availability of a central mapping between EquipmentId + Tag to an inmation path. Therefore the function calls have been extended to include the mapping table from the Message Processor. So that the Input Script/Input Function, as well as the Output Scrip/Output Function can read the central mapping table.