0% found this document useful (0 votes)
44 views83 pages

VNode Manual Scripting v1.6

The vNode Scripting module enables the execution of custom JavaScript logic in response to various triggers, allowing interaction with the vNode API and Node.js libraries. It supports unlimited scripts and tasks, each with configurable actions such as startup, shutdown, and periodic execution. Users can define scripts with parameters and utilize shared libraries for enhanced functionality.

Uploaded by

engacosta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
44 views83 pages

VNode Manual Scripting v1.6

The vNode Scripting module enables the execution of custom JavaScript logic in response to various triggers, allowing interaction with the vNode API and Node.js libraries. It supports unlimited scripts and tasks, each with configurable actions such as startup, shutdown, and periodic execution. Users can define scripts with parameters and utilize shared libraries for enhanced functionality.

Uploaded by

engacosta
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 83

1

Reference Manual
vNode Scripting
v1.6

www.vnodeautomation.com
2

Index
Introduction .................................................................................................................................................... 3

Configuration ................................................................................................................................................. 4
Tasks .............................................................................................................................................................. 4
Actions .......................................................................................................................................................... 4
Scripts ...........................................................................................................................................................11
Libraries ......................................................................................................................................................17
Source ..........................................................................................................................................................19

API ..................................................................................................................................................................... 20

Installing NPM libraries ......................................................................................................................... 66

Examples ........................................................................................................................................................ 73

www.vnodeautomation.com
I
3

Introduction

vNode’s Scripting module allows the execution of custom logic within the current vNode
node in response to various triggers, such as timers, tag changes, tag conditions, and
more. This logic is implemented using a native JavaScript interpreter (Node.js) capable of
executing user-provided code and granting access to both Node.js native libraries (e.g., fs,
net, or HTTP) and the extensive Node.js ecosystem of libraries (e.g., through NPM).
Furthermore, Scripting has complete access to the vNode API, enabling direct interaction
with tags (including real-time and historical data), alarms, retrieval of the
current vNode node's status, module status, and many other features.

The main features that this module offers include:

• Unlimited number of scripts, each running in isolation on a separate OS thread.

• Numerous triggers for scripts, including Startup, Periodic or Tag Change.

• Unlimited access to the vNode API.

• Access to both the native Node.js libraries as well as its entire ecosystem of
packages.

• Easy user configuration for scripts using parameters.

www.vnodeautomation.com
I
4

Configuration

Note: Before starting configuration, a new module instance must be created.


Each module has an API section that need to be configured separately. The
default settings will be sufficient for this, but users will need to actively open the
API configuration settings and save the default values to fully apply the settings.

Tasks
A task is a collection of different actions, each with its own trigger, which can execute one
or several scripts. While the scripts in each task are independent, they can share data
within the same task by using $.taskLocal and between tasks by using $.global:

Figure 1. Task options

Each task can be individually enabled or disabled by configuring the “Enabled” option. If
the task is disabled, none of the actions in the task will be triggered and the scripts will
not be executed. Each task can have an unlimited number of actions.

Actions
An action is an object that can have several scripts, as well as a trigger, which will start the
execution of these scripts. There are 6 different types of actions, each with their own
configuration settings:

• Start-up: Executes scripts when the module starts.

www.vnodeautomation.com
I
5

• Shutdown: Executes scripts when the module shuts down.

• Periodic: Executes scripts after a configurable period.

• Change: Executes scripts when the specified tag changes.

• Condition: Executes scripts when the specified tag fulfills a condition.

• External: Never executes by itself. Will execute when called by external modules.

Start-up, shutdown, and external do not require any specialized configuration, whilst the
others will require different configuration settings to be applied.

Periodic

Periodic actions offer the following configuration options:

Figure 2. Periodic options

• Period: Specifies the time period for this action, displayed in ms. The default value is
10 seconds.
• Period type: Selects how the period will be calculated. Valid values are:
o Fixed delay: The scripts trigger after the specified period in ms has elapsed since
the scripts have completely finished executing. For example, if the scripts take
250 ms to complete execution, the period is set to 10,000 ms, and the current time
is 00:00:00.000, the first execution will be at 00:00:10.000, the second execution
at 00:00:20:250, and the third at 00:00:30.500 and so forth.
o Fixed rate: The scripts trigger after the specified period in ms has elapsed since
the last trigger, independently of when the script finished its execution. For
example, if the scripts take 250 ms to complete execution, the period is set to

www.vnodeautomation.com
I
6

10,000 ms, and the current time is 00:00:02.463, the first execution will be at
00:00:12.463, the second execution at 00:00:22:463, and the third at 00:00:32.463
and so forth. If the scripts don't finish before the next period triggers, the script
will start as soon as the previous execution finishes.
o Fixed interval: The action starts at fixed intervals depending on the period,
independently of when the script finished execution. If the specified period
elapses before the script finishes, all missed intervals will be skipped. For example,
if the period is 10,000 ms, the script will always execute at 00:10:00.000,
00:20:00.000, etc., independently of when the script ends. If execution of the
script takes longer than the specified period, enough intervals are skipped to
allow the script to execute at the next interval.
o Cron: The action starts periodically based on a cron expression. If the script doesn't
finish before the next scheduled time, this execution will be skipped.

Change

Tag change actions offer the following configuration options:

Figure 3. Tag change options

• Trigger type: Selects between the different trigger types for the tag change action.

The available types are as follows:

o Single tag: This trigger type will execute the script whenever the specified tag
generates a tag event.

www.vnodeautomation.com
I
7

Figure 3.1. Single tag trigger

o Tag group: This trigger type will execute the script whenever any of the tags
within the group generate a tag event.

Figure 3.2. Tag group trigger

o Multiple tags (Filter): This trigger will execute the script whenever any of the
tags that meet the filter conditions generate a tag event.

www.vnodeautomation.com
I
8

Figure 3.3. Multiple tags trigger

• Trigger options:

o Property: Selects which of the tag’s properties should be monitored for changes.
The valid values are Any, Value, Quality, or Timestamp.

o Initial change: If enabled, the initial event after a tag subscription can trigger the
scripts. Otherwise, the initial event is discarded.

o Maximum event buffer size: Specifies the maximum number of events that will be
buffered in case the events are received faster than the script can execute. After
the buffer fills up, the oldest events will be discarded. Otherwise, the script will be
triggered once for each event in the buffer. The minimum value is 1, which means
that the script will always execute using the last event received.

Condition

Condition actions offer the following configuration options:

Figure 4. Condition options

www.vnodeautomation.com
I
9

• Tag: Selects which tag will be monitored by this action. Each condition action can
only monitor one tag.

• Property: Selects which tag property will be used to check the condition. The valid
values are Value, Quality, and Timestamp.

• Initial change: If enabled, the initial event after a tag subscription can trigger the
condition check. Otherwise, the initial event is discarded.

• Condition: Selects the condition that must be met for the scripts to trigger. The valid
values are:

o Null: Triggers if the tag property is null.

o Not null: Triggers if the tag property is not null.

o Equal to: Triggers if the tag property is equal to the specified value.

o Not equal to: Triggers if the tag property is not equal to the specified value.

o Greater than: Triggers if the tag property is greater than (but not equal to) the
specified value.

o Greater than or equal: Triggers if the tag property is greater than or equal to the
specified value.

o Less than: Triggers if the tag property is less than (but not equal to) the specified
value.

o Less than or equal: Triggers if the tag property is less than or equal to the specified
value.

• Value: Specifies the value that will be used along with the condition to see if the
script needs to trigger.

• Condition type: Selects how the condition applies. The valid values are:

o If true: The scripts will trigger when the condition becomes true.

o If false: The scripts will trigger when the condition becomes false.

o While true: The scripts will trigger once when the condition becomes true and
then trigger again periodically while the condition remains true.

o While false: The scripts will trigger once when the condition becomes false and
then trigger again periodically while the condition remains false.

If the condition type is either If true or If false, the following two additional configuration
options are shown:

• Trigger on update: If enabled, scripts will trigger every time a new tag event is
received while the condition remains true. Otherwise, the scripts will not trigger again
until after the condition is cleared.

• Maximum event buffer size: Specifies the maximum number of events that will be
buffered in case the events are received faster than the script can execute. When the

www.vnodeautomation.com
I
10

buffer becomes full, the oldest events will be discarded. Otherwise, the script will be
triggered once for each event in the buffer. The minimum value is 1, which means that
the script will always execute using the last event received.

If the condition type is While true or While false, the following two additional
configuration options are shown:

• Period: Specifies the period between script executions, displayed in ms. The default
value is 10 seconds.

• Period type: Selects how the period will be calculated. The valid values are:

o Fixed delay: The scripts trigger after the specified period in ms has passed since
the scripts have completely finished executing. For example, if the scripts take
250 ms to complete execution, the period is set to 10,000 ms, and the current time
is 00:00:00.000, the first execution will be at 00:00:10.000, the second execution
at 00:00:20:250, and the third at 00:00:30.500 and so on.

o Fixed rate: The scripts trigger after the specified period in ms has passed since the
last trigger, independently of the time when the script finished its execution. For
example, if the scripts take 250 ms to complete execution, the period is set to
10,000 ms and the current time is 00:00:02.463, the first execution will be at
00:00:12.463, the second execution at 00:00:22:463, and the third at 00:00:32.463
and so on. If the scripts don't finish before the next period triggers, the script will
start as soon as the previous execution finishes.

o Fixed interval: The action starts at fixed intervals depending on the period,
independently of when the script finished its execution. If this period elapses
before the script finishes, all missed intervals will be skipped. For example, if the
period is 10,000 ms, the script will always execute at 00:10:00.000, 00:20:00.000,
etc., independently of when the script ends. If execution of the script takes longer
than the specified period, enough intervals are skipped to allow the script to
execute at the next interval.

o Cron: The action starts periodically based on a cron expression. If the script doesn't
finish before the next scheduled time, that execution will be skipped.

External

External actions are different from normal actions, they never invoke the script, instead
they wait to be called from another vNode module, such as via OpcUaServer methods.
These methods receive the arguments passed from the module that calls them and return
a value back to the module that called them.

www.vnodeautomation.com
I
11

When calling an external action, it takes a single object argument containing key-value
pairs, where each key is the argument name and the value is the argument value:

//Object argument when calling external script


{
arg1: "String",
arg2: 123,
boolArg: true
}

//Resulting $.event object in the script


$.event = {
arg1: "String",
arg2: 123.
boolArg: true
}

The return value can be any anything and depends on what the calling module is
expecting. For example, OpcUaServer expects the return value to be an object where each
key is the output argument name and each value is the value of that output argument.
More detail about the specific return values can be found in the documentation for all
modules that allow calling external scripts.

Scripts
A script is user defined JavaScript code that will execute every time an action triggers.
Each action can have one or more scripts.

Each script has access to several objects and functions that are used to interface with
vNode and to share data between different scripts in the same task or between different
tasks. All of these objects are detailed in the API section of this manual.

Scripts also have access to all Node.js native libraries (such as fs or net), as well as user
defined libraries.

Each script has the following options:

www.vnodeautomation.com
I
12

Figure 5. Script options

• Enabled: Enables and disables execution of this script.

• Script: Contains the script that will be executed. By clicking the code button, a code
editor will be displayed where the user code can be entered.

• Parameters: These parameters are passed to the script in the $.parameters object.
They are usually used to define a configuration that is then used for the script. For
example, a parameter can be used to set a basepath that will be used in the script, or
the host + port combination used to open a socket in the script. These parameters are
read-only. If they are written, they will throw up an exception. Parameters can be one
of the following types:

o String: Represents a JavaScript string.

www.vnodeautomation.com
I
13

o Number: Represent a JavaScript number, which is defined as a 64 bits floating


point number (except that only finite values are allowed, meaning that neither
NaN nor ± Infinite are permitted).

o Boolean: Represents a Javascript boolean data type, which can be


either true or false.

o Array: Represents a JavaScript array, which is an ordered collection of elements.


By clicking on New ArrayElement, users can add elements to the array, each with
a specific type (including nesting arrays and objects).

www.vnodeautomation.com
I
14

o Object: Represents a JavaScript object, which is a key-value collection. Each


object can have multiple properties that can be the same types as the parameters,
including arrays and objects, meaning that it can be used to build nested data
structures.

www.vnodeautomation.com
I
15

o TagPath: Users can select a tag previously created in the Tags section by clicking
on the blue button on the right-hand side of the Value field.

www.vnodeautomation.com
I
16

www.vnodeautomation.com
I
17

Libraries
This section allows users to declare shared libraries that can then be used by all the tasks
in this Scripting instance. There are three types of libraries, as seen in the following
screenshot:

Figure 6. Internal library configuration

• Internal libraries: These libraries are bundled with vNode and as such, can be loaded
directly without installing anything more. The internal libraries available to Scripting
are:
o Cron: This library is used to validate a given cron expression, as well as retrieving
the next time the expression has to be executed. More information can be found
at cron.
o JsPDF: This library is used to generate PDF documents using JavaScript. The
included version is X. More information can be found at JsPDF.
o JSZip: This library is used to read and write .zip files. The included version is 3.1.4.
More information can be found at JSZip.
o LZ4: This library is used to compress data using the LZ4 algorithm. The included
version is 0.5.3. More information can be found at LZ4.
o Moment.js: This library is used to parse and format dates & times. This library
version includes both moment (version 2.23.0) and moment-timezone (version
0.5.23). More information can be found at Moment and Moment Timezone.
o MongoDB: This library is used to connect and execute queries to a MongoDB
database. The included version is 2.2.36. More information can be found at
MongodB.
o MQTT.js: This library is used to communicate with a MQTT broker using the MQTT
protocol. The included version is 2.2.1. More information can be found at MQTT.js.

www.vnodeautomation.com
I
18

o MySQL: This library is used to connect to a MySQL database and execute queries.
The included version is 2.17.1. More information can be found at MySQL.
o PostgreSQL: This library is used to connect to a PostgreSQL database and execute
queries. The included version is 6.4.2. More information can be found at
PostgreSQL.
o Protobuf.js: This library is used to serialize and parse messages using Google’s
Protocol Buffers format. The included version is 6.8.0. More information can be
found at Protobuf.js.
o SQL Server: This library is used to connect to a SQL Server database and execute
queries. The included version is 3.3.0. More information can be found at SQL
Server.
o WebSockets: This library is used to create a WebSocket client and server. The
included version is 1.1.0. More information can be found at WebSockets.
o XLSX Writer: This library is used to write data to XLSX files.
o Xml2Js: This library is used to serialize and parse XML. The included version is
0.4.16. More information can be found at Xml2Js.

• External libraries: These libraries are external to vNode and must be installed by the
user. They can be installed manually, but the recommended method is through the
Node Package Manager (npm). More information about install libraries can be found
here. When using external libraries, this is either the path of the library entry point
(index.js and main.js being the typical entry points) or the path to the folder containing
the package.json file (typically, node_modules/LIBRARY_NAME).

Note: External libraries must be compatible with Node 12.22.10. Additionally,


native modules may or may not work, depending on whether they are
declared as "Context-aware".

• Static libraries: These libraries contain user defined code directly in vNode. A use case
for these libraries is creating one or multiple classes that are shared across different
scripts. These libraries have full access to the same API as the rest of Scripting and
they are also saved to vNode backups (unlike external libraries, which are not saved).
In order to export data from a static library, the module.exports syntax is used.

www.vnodeautomation.com
I
19

Source
Scripting can also act as a data provider for tags. To do this, all new tags will need to be
configured with Scripting as their source, as seen in the following screenshot:

Figure 7. Scripting source tag configuration

Scripting source tags accept a single optional parameter in the form of an alias that will
be used to reference the tag when providing data for that tag. If provided, the tag can be
updated using said alias. Otherwise, the full path will need to be used. The full path can be
used regardless of whether a tag has an alias or not. In order to update a tag, the
$.api.tag.update or $.api.tag.updateAlias functions must be called.

www.vnodeautomation.com
I
20

API
Each script has access to the following features that are not part of the Javascript
standard. Instead, these are added by vNode. Most of them are accesed via a special
$ object. However, some (like the sprintf function) can be used directly:

sprintf( format: string, ...params: any[])

Sprintf is a function that can be used to create a new string from a format string that can
contain placeholders (for example %s and %d) and a list of values that will replace the
placeholders in the format string in order to generate the new string. The following are
valid placeholders for different data types:

• Integer: %d or %i
• String: %s
• Binary: %b
• Boolean: %t
• JSON: %j
• ASCII character from numeric value: %c
• Scientific notation: %e
• Floating point: %f
• Fixed point: %g
• Octal: %o
• Unsigned integer: %u
• Hexadecimal lowercase: %x
• Hexadecimal uppercase: %X
• Node buffer: %r

If the integer and unsigned integer format types receive a decimal number, the decimal part
will be truncated.

Most of these placeholders can also accept modifiers that will affect how the string will be
formatted. For example, %02f will force a floating point number to always use 2 decimals.
More information about the sprintf function can be found at NPM sprintf-js.

An example of various format strings with different placeholders can be seen in the next
code snippet:

www.vnodeautomation.com
I
21

$.sprintf("Numbers are: %d and %i", 10, 15.7)


//Numbers are: 10 and 15

$.sprintf("String is: %s", "Hello world!");


//String is: Hello world!

$.sprintf("The binary representation of 5 is: %b", 5);


// The binary representation of 5 is: 101

$.sprintf("The ASCII character with decimal value 48 is %c", 48);


// The ASCII character with decimal value 48 is 0

$.sprintf("1000 in scientific notation is: %e", 1000);


1000 in scientific notation is: 1e3

$.sprintf("1234 in scientific notation and 2 decimals is: %.2e", 1234);


//1234 in scientific notation and 2 decimals is: 1.23e3

$.sprintf("12.34 in floating point notation is: %f", 12.34)


//12.34 in floating point notation is: 12.34

$.sprintf("12.3456 in fixed point notation is: %.3g", 12.3456);


//12.3456 in fixed point notation is: 12.3

$.sprintf("12 in octal is: %o", 12);


//12 in octal is: 14

$.sprintf("-10 in unsigned integer format is: %u", -10)


//-10 in unsigned integer format is: 4294967286

$.sprintf("30 in hexadecimal is: %x", 30);


//10 in hexadecimal is: 1e

$.sprintf("30 in uppercase hexadecimal is: %X", 30);


//30 in uppercase hexadecimal is: 1E

const buf = Buffer.from("Hello world!");


$.sprintf("The buffer is: %r", buf);
//The buffer is: <48 65 6c 6c 6f 20 77 6f 72 6c 64 21>;

$.global

The $.global object is a key-value store that can be used to share data between all the
scripts, even if they are in different tasks. It has two functions:
• get(property: string) => any: This function is used to retrieve a value from the global
store.
• set(property: string, value: any) => void: This function is used to store a value with a given
key in the global store. Only values that can be serialized using the structured clone
algorithm can be stored in the global store. This includes number, strings, booleans,
objects (without any associated functions), maps and sets. If a value can't be serialized,
an error is thrown. Some common variables that can not be serialized are class
instances and functions.

www.vnodeautomation.com
I
22

$.taskLocal

The $.taskLocal object is a key-key store shared between all the scripts within the same
task, but not between scripts in different tasks. It has two functions:
• get(property: string) => any: This function is used to retrieve a value from the global
store.
• set(property: string, value: any) => void: This function is used to store a value with a
given key in the global store. Only values that can be serialized using the structured
clone algorithm can be stored in the global store. This includes number, strings,
booleans, objects (without any associated functions), maps and sets. If a value can't
be serialized, an error is thrown. Some common variables that can not be serialized
are class instances and functions.

$.local

The $.local is a persistent object within a specific script. This can be used to store state
between script executions. Unlike $.global and $.taskLocal, since this is an object, it can
store any value that can be stored in an object (including functions or class instances).
These values do not persist between module restarts.

$.lib

$.lib is a function used to import libraries declared in the Libraries configuration in a Script.
This is done by invoking the $.lib(name: string) => any function with the same name that
was given when declaring the library in the configuration section.

$.paths

The $.paths object contains different paths for vNode, irrespective of where vNode is
installed or the operating system. The $.paths object has two nested objects, one labelled
global and one labelled local. The first one contains paths relevant to the vNode
installation, while the latter contains paths related to the current Scripting instance.
For example, if vNode is installed in the default windows installation folder and the current
Scripting instance named as “Scripting”, the $.paths object has the following contents:

www.vnodeautomation.com
I
23

{
base: C:\Program Files\vNode,
global: {
bin: C:\Program Files\vNode\bin,
config: C:\Program Files\vNode\config,
log: C:\Program Files\vNode\log,
data: C:\Program Files\vNode\data,
licenses: C:\Program Files\vNode\licenses
},
local: {
bin: C:\Program Files\vNode\bin\modules\Scripting,
config: C:\Program Files\vNode\config\Scripting,
log: C:\Program Files\vNode\log\Scripting,
data: C:\Program Files\vNode\data\Scripting
}
}

$.exit(error?: null|Error, returnValue?: any)

The $.exit() function is used to indicate that the current script execution has finished. This
function must be called when the script has executed completely (that includes both
synchronous and asynchronous executions), and after it is called, no further code should
be executed. As such, it is recommended to follow a call to $.exit() with the return;
statement to stop the script from executing any more code.

This function takes two optional parameters. If the function is called without parameters,
it is assumed that it finished without errors and without a return value:

• error: If set, the error will be automatically logged to the log. If there are no errors and
the script has a return value, the error argument must be set to null.
• returnValue: When set, this will be the return value of the script. This is mostly used
by external actions in order to return values to other modules. The return value can
have any type, although other modules may impose a specific format for the return
value in order to function properly.

$.event

The $.event object contains information about the trigger that launched the current script.
This object will vary depending on which type of action launched the script, as seen in the
following list:

• Start-up
o $.event.startTime: Contains the time when the module was started, in UNIX epoch
with milliseconds.
• Shutdown
o $.event.stopTime: Contains the time when the module was stopped, in UNIX
epoch with milliseconds.

www.vnodeautomation.com
I
24

• Periodic
o $.event.time: Contains the time when the periodic action was triggered, in UNIX
epoch with milliseconds.
o $.event.initial: Boolean flag that indicates if the current execution is the initial one
(when Run on startup is true) or a normal periodic execution.
o $.event.blockTs: Contains the start time of the current execution rounded to the
nearest interval (only applies to interval and cron period types). For example, if the
interval is 60 seconds, or the cron job runs every minute, the blockTs will be
rounded to XX:01:00, XX:02:00, even if the script executes slightly later due to timer
delays.

• Tag change
o $.event.tag: Contains the full path of the tag that changed, as a string.
o $.event.oldValue: Contains the old event of the tag before the change. It's an
object with value (whose type depends on the tag type), quality (as a number) and
ts (as a number in UNIX Epoch milliseconds).
o $.event.newValue: Contains the new event of the tag which triggered the script.
It's an object with value (whose type depends on the tag type), quality (as a
number) and ts (as a number in UNIX Epoch milliseconds).
o $.event.changes: Contains an object indicating which properties have changed
in the new tag event compared to the old tag event. It has three boolean
properties, a value, quality, and timestamp. which are set to true if the property
has changed, or false if it hasn't changed.
• Condition
o $.event.tag: Contains the full path of the tag that triggered the condition, as a
string.
o $.event.value: Contains the event that triggered the condition, or the current
value if the condition type is whileTrue or whileFalse. It's an object with value
(whose type depends on the tag type), quality (as a number) and ts (as a number
in UNIX Epoch milliseconds).
o $.event.time: Contains the time when the periodic tag condition action was
triggered, in UNIX epoch with milliseconds. Only applies if the condition type
is whileTrue or whileFalse.
o $.event.initial: Boolean flag that indicates if the current execution is the initial one
(when the condition first triggers), or a normal periodic execution. Only applies if
the condition type is whileTrue or whileFalse.
o $.event.blockTs: Contains the start time of the current execution rounded to the
nearest interval (only applies to interval and cron period types). For example, if the
interval is 60 seconds, or the cron job runs every minute, the blockTs will be
rounded to XX:01:00, XX:02:00, even if the script executes slightly later due to timer
delays. Only applies if the condition type is whileTrue or whileFalse.

www.vnodeautomation.com
I
25

• External action
o External actions contain their arguments as keys to the $.event object. For
example, if an external action receives value1=23 and value2=60as arguments, the
event object will contain $.event.value1 and $.event.value2.

The following code block contains examples of all the different $.event objects:

//Startup at 2022-04-25 10:00:00


$.event.startTime = 1650880800000;

//Shutdown at 2022-04-25 10:30:00


$.event.stopTime = 1650882600000;

//Periodic trigger at 2022-04-25 10:00:23 with 60s interval


$.event.time = 1650880823000;
$.initial = false
$.event.blockTs = 1650880800000;

//Tag /Plant1/ActivePower changes from 1000 to 1020


$.event.tag = "/Plant1/ActivePower";
$.event.oldValue = {value:1000, quality: 192, ts: 1650880772000};
$.event.newValue = {value:1020, quality: 192, ts: 1650880810000};
$.event.changes = {value:true, quality: false, ts: true};

//Tag /Plant1/Alarm changes from 0 to 1, triggering a condition


$.event.tag = "/Plant1/Alarm";
$.event.value = {value:1, quality:192, ts: 1650880810000};

//External action with val1=123 and val2=300 arguments


$.event.val1 = 123;
$.event.val2 = 300;

$.config

The $.config object contains static information about the action that triggered the script.
This object contains the following keys:

• $.config.type: An enumeration that identifies the action that triggered this script. The
values for this enumeration are the following:
o Start-up: 0
o Shutdown: 1
o Periodic: 2
o Tag Condition: 3
o Tag change: 4
o External: 5

• $.config.actionOptions: An object that contains the configuration of the action that


triggered this script, which depends on the type of action (and is non-existent for
Startup, Shutdown and External, since these actions don't have any specific
configuration).

www.vnodeautomation.com
I
26

$.parameters

The $.parameters object is used to retrieve configuration parameters for the current script.
The structure of this object is the same as the one in the configuration. For example, if
there is a parameter named "Basepath", it's value can be retrieved by using
$.parameters.Basepath. These parameters are read-only and will throw an error when
attempting to modify them (this includes functions that modified them as a side effect,
such as the .push() method of an array).

$.logger

The $.logger object is used to log messages to disk and can be used for both debugging
and informative purposes. The log file can be found in vNode/log/ScriptingInstance/. It is
shared by both the internal module code and any messages written by users, which can
be easily distinguished by the “User” string appended to them. The logger has five
different logging levels:
• $.logger.error(msg: string, ...params: any[])
• $.logger.warn(msg: string, ...params: any[])
• $.logger.info(msg: string, ...params: any[])
• $.logger.debug(msg: string, ...params: any[])
• $.logger.trace(msg: string, ...params: any[])

Each logging function takes at least one argument, containing the string that is going to
be logged. This string can also be a format string with placeholder, in which case the rest
of the arguments will be used to resolve these placeholders. This is the same behaviour as
sprintf.

$.api

The $.api object is used to interface with vNode, and provides access to several of vNode
functionalities, such as tags, links, vNode node stats, etc. Each of these functions is hosted
in a different namespace, depending on which part of vNode they affect. For example,
functions that affect tags are hosted in the tag namespace, functions that provide the
status of a host machine are present in the system namespace, and so on:

Most of the functions in $.api execute asynchronously and return a promise that is fulfilled
when the asynchronous request finishes. This can also be used with the await keyword
function straight to the top level, without needing to wrap it into an async function. These
functions can also reject promises in case of an error, in which case, the rejection reason
is a string with information about the error.

www.vnodeautomation.com
I
27

The different subspaces and their functions are as follows:

$.api.authentication

$.api.authentication.getRoles
Function prototype: $.api.authentication.getRoles(options: {}): Promise<string[]>

This function retrieves the user roles available on the target node.

Example:
const roles = await $.api.authentication.getRoles({});

for (const roleName of roles) {


$.logger.info("Found role '%s'", roleName);
}

$.api.authentication.get
Function prototype: get(user: string, password: string): Promise<{id: string, name: string,
roles: string[]}>

This function performs authentication for a specific user and returns its internal ID and the
roles the user belongs to.

Example:
const { id, name, roles } = await $.api.authentication.get("admin", "vNode");

$.logger.info("ID: %s, Name: %s, Roles => %s", id, name, JSON.stringify(roles));

www.vnodeautomation.com
I
28

$.api.system

$.api.system.status

Function prototype: $.api.system.status(options: {}): Promise<StatusResult>

This function queries the status of the host system, and returns an object with information
about the current hardware, the host name, uptime, etc.

Example:

const statusResult = await $.api.system.status({});

$.logger.info(JSON.stringify(statusResult));

The status result is an object with the following structure:


type StatusResult = {Node001:{errors: ReadonlyArray<string>,node: string,pid: number,
tags: {local: number,remote: number,total: number},up: number,versions: [exeVersion:
string, bootstrapVersion: string]},sys: {cpu: [model: string, frequency: number,
coreCount: number],hostname: string,standby: boolean,ts: number,up: number,usage: {cpu:
[used: number, total: number],disk: [used: number, total: number],ram: [used: number,
total: number]}}}

$.api.system.errorCount

Function prototype: $.api.system.errorCount(options: {}): Promise<number>

This function queries the number of errors in the current node. If there are no errors, 0 is
returned, otherwise, it returns the number of errors (this can be caused by invalid tag
configuration, invalid link configuration, etc...).

Example:

const errCount = await $.api.system.errorCount({});

$.logger.info("The current node has %d errors", errCount);

www.vnodeautomation.com
I
29

$.api.system.getStandby

Function prototype: $.api.system.getStandby(options: {}): Promise<boolean>

This function queries the standby status of the current node. If the node does not use
redundancy, or if it's the standby node, the function returns false, otherwise, it returns true.

Example:

const isStandby = await $.api.system.getStandby({});


if (isStandby) {
$.logger.info("Node is in standby mode");
} else {
$.logger.info("Node is NOT in standby mode");
}

$.api.system.getNetworkAdapters

Function prototype:
$.api.system.getNetworkAdapters(options: {}) Promise<NetworkInterface[]>

This function retrieves the network interfaces available in the host system.

Example:

const adapters = await $.api.system.getNetworkAdapters({});

$.logger.info(JSON.stringify(adapters));

The NetworkInterface is an object with the following structure:

type NetworkInterface = {all: boolean;name: string;address: string;mask: string;mac:


string;internal: boolean;}

$.api.system.restartService

Function prototype: $.api.system.restartService(options: {})


This function restarts the vNode service.

Example:

www.vnodeautomation.com
I
30

$.api.system.restartService({});

$.api.license

$.api.license.getUid
Function prototype: $.api.license.getUid(): Promise<UidObject>

This function retrieves the UID of the vNode installation.

Example:

const { host, uid } = await $.api.license.getUid();

$.logger.info("Host: %s, UID: %s", host, uid);

It returns a UidObject containing the host and UID.

type UidObject = {
host: string,
uid: string
}

$.api.license.get
Function prototype: $.api.license.get(): Promise<ReadonlyArray<LicenseFile>>

This function retrieves the list of licenses installed in the target node.

Example:

const licenses = await $.api.license.get();

for (const lic of licenses) {


$.logger.info(JSON.stringify(lic));
}

www.vnodeautomation.com
I
31

It returns a ReadonlyArray<LicenseFile> with the following definition:

type LicenseFile = {
name: string,
error: string,
id: string,
host: string,
uid: string
nonce: number,
unlicensed: boolean,
licenses: ReadonlyArray<License>,
}
type License = {
version: string,
error: string,
host: string,
uid: string,
expires: string,
type: "production",
modules: ReadonlyArray<ModuleLicense>,
supportExpires: number,
nonce: number,
id: string
}
type ModuleLicense = {
type: string,
tagAccess: "remote"|"local",
count: number,
used: number
}

$.api.license.read

Function prototype: $.api.license.read(name: string): Promise<string>


This function returns the content of a license.
• name: The name of the license.

Example:

const license = await $.api.license.read("node001-20240213.105718.n3l")

$.logger.info(license);

$.api.license.add

Function prototype: $.api.licenses.add(data: string, options:{}): void;


This function adds a new license to the target node. Receives the content of the license
file.
• data: The full content of the license file.

www.vnodeautomation.com
I
32

Example:

$.api.license.add(`{"version":"1","created":"2024-02-13T10:57:00.104Z", ....}`, {});

$.api.license.remove

Function prototype: $.api.licenses.remove(name: string, options:{}): void;


This function removes a license from the target node.
• name: The name of the license.

Example:

const license = "node001-20240213.105718.n3l";

try {
await $.api.license.remove(license, {});
$.logger.info("License '%s' removed", license);
} catch (error) {
$.logger.error("Failed to remove license: %v", error);
}

$.api.license.unlicense

Function prototype: $.api.license.unlicense(name: string, options:{}): Promise<string>


This function unlicenses the node and returns the unlicense file encoded as Base64.
• name: The name of the license.

Example:

try {
const unlic = await $.api.license.unlicense(
"node001-20240213.105718.n3l",
{}
);
$.logger.info("Unlicense file ==> %s", unlic);
} catch (error) {
$.logger.error("Failed to unlicense: %v", error);
}

www.vnodeautomation.com
I
33

$.api.link

$.api.link.get

Function prototype: $.api.link.get(): Promise<LinkStatus>


This function retrieves information about the status of the links in the current node.

Example:

const linkStatus = await $.api.link.get();

$.logger.info(JSON.stringify(linkStatus));

The return value is a promise that resolves an object with the following structure:

type LinkStatus = {
nodeInfo: {
name: string
},
links: {
[linkName:string]: {
local: {
subscription: null | string,
tagCount: number,
view: {
name: string,
ro: boolean,
ae: boolean,
ack: boolean
},
sf: {
enabled: boolean,
paused: boolean
}
},
name: string,
online: boolean,
remote: null | {
endpoint: string,
subscription: null | string,
tagCount: number,
view: {
name: string,
ro: boolean,
ae: boolean,
ack: boolean
},
sf: {
enabled: boolean,
paused: boolean
}
},
type: "in"|"out"
}
}
}

www.vnodeautomation.com
I
34

$.api.link.sfPause

Function prototype: $.api.link.sfPause(link: string, paused: boolean): void

This function pauses the Store&Forward mechanisms for a specific link. When paused, no
more events from S&F will be sent over that link until it is unpaused. This can be used if
the receiver gets saturated with events from a link. By pausing the link, it can process the
events it received before unpausing it to receive more events. It takes two arguments:

o link: This is the name of the link that will be paused.

o paused: When true, it pauses the link. When false, it unpauses the link.

Example:

$.api.link.sfPause("central01", true);

$.api.certificate

$.api.certificate.get

Function prototype: $.api.certificate.get(options: {moduleName: string, type: string}):


Promise<Array<Certificates>>

This function is used to retrieve certificates with a specific type from a module. It takes a
single argument:

o options: Options used to select which certificates to retrieve.

▪ moduleName: Name of the module whose certificates will be retrieved. Can


be set to "bootstrap" to retrieve bootstrap link certificates

▪ type: Selects which type of certificate will be retrieved. For bootstrap, it can
be "outbound" or "inbound", while other modules can define their own
types. The types of certificate for other modules can be found in their
documentation.

Example:

const certificates = await $.api.certificate.get({


moduleName: "bootstrap",
type: "outbound",
});

for (const cert of certificates) {


$.logger.info(JSON.stringify(cert));
}

www.vnodeautomation.com
I
35

The return value is a promise which resolves to an array of the following object:

type Certificate = {
c: string,
dn: string,
from: number,
id: string,
o: string,
sn: string,
to: number,
type: string,
status: string
}

$.api.certificate.set

Function prototype: $.api.certificate.set(options: {moduleName: string, type: string, id:


string, action: string}): Promise<void>

This function is used to execute an action against a certificate. It takes a single argument:

o options: Options used to select the target certificate and action that will be applied.

▪ moduleName: Name of the module whose certificates will be modified. Can


be set to "bootstrap" to execute actions on bootstrap link certificates.

▪ type: Select the type of certificate that will be affected by the action. For
bootstrap, it can be "outbound" or "inbound", while other modules can
define their own types. The types of certificates for other modules can be
found in their documentation.

▪ id: Select which certificate will be affected by the action. The id can be
retrieved by a call to $.api.certificate.get.

▪ action: Select which action will be executed. The available actions depend
on the module and the state of the target certificate (for example, a
certificate that is already trusted can't be trusted again). The actions
available to bootstrap link certificates are "clear", "reject", and "trust".

Example:

await $.api.certificate.set({
moduleName: "bootstrap",
type: "outbound",
id: "pending/CENTRAL01__LinkManager[F07A91C73030507C4ABCB3689C46257A6E9406A5]",
action: "trust",
});

www.vnodeautomation.com
I
36

$.api.certificate.export

Function prototype: $.api.certificate.export(options: {moduleName: string, type: string, id:


string, action: string} ): Promise<string>

This function is used to export a certificate. It takes a single argument:

o options: Options used to select the target certificate that will be exported.

▪ moduleName: Name of the module whose certificate will be exported. Can


be set to "bootstrap" to export bootstrap link certificates.

▪ type: Selects the type of certificate that will be exported. For bootstrap, it
can be "outbound" or "inbound", while other modules can define their own
types. The types of certificate for other modules can be found in their
documentation.

▪ id: Selects which certificate will be exported. The id can be retrieved by a call
to $.api.certificate.get.

Example:

const cert = await $.api.certificate.export({


moduleName: "bootstrap",
type: "outbound",
id: "own/NODE01__ LinkOut",
});

$.logger.debug(cert);

The return value is a promise that resolves to a base64 string of the certificate's contents.

$.api.certificate.remove

Function prototype: $.api.certificate.remove(options: {moduleName: string, type: string, id:


string, action: string} ): Promise<void>

This function is used to remove a certificate. It takes a single argument:

o options: Options used to select the target certificate that will be removed.

▪ moduleName: Name of the module whose certificate will be removed. Can


be set to "bootstrap" to remove bootstrap link certificates.

▪ type: Select the type of certificate that will be removed. For bootstrap, it can
be "outbound" or "inbound", while other modules can define their own
types. The type of certificate for other modules can be found in their
documentation.

www.vnodeautomation.com
I
37

▪ id: Select which certificate will be removed. The id can be retrieved by a call
to $.api.certificate.get.

Example:
await $.api.certificate.remove({
moduleName: "bootstrap",
type: "outbound",
id: "pending/CENTRAL__LinkManager[F07A91C73030507C4ABCB3689C46257A6E9406A5]",
action: "remove",
});

$.api.module

$.api.module.details

Function prototype: $.api.module.details(): Promise<ModuleDetails>

This function is used to retrieve details about the current node (such as uptime, RAM
usage, etc.) as well as details of the created modules (such as running status, license status,
etc.). It returns a promise that resolves to the following object:

Example:

const details = await $.api.module.details();

$.logger.info(JSON.stringify(details));

www.vnodeautomation.com
I
38

type ModuleDetails = {
memoryUsage: {
arrayBuffer: string,
external: string,
heapTotal: string,
heapUsed: string,
rss: string
},
modules: {
moduleName: string]: {
api: null | {
online: boolean,
info: {
category: Array<"source"|"publisher">,
fileversion: string,
name: string,
releaseType: string,
requisites: {
exeVersion: undefined | {min: string, max: string},
bootstrapVersion: undefined | {min: string, max: string}
}
type: string,
version: string
},
stats: {
tx: {ok: number, error: number},
rx: {ok: number, error: number}
}
}
config: {
required: boolean,
type: string,
start: {
enabled: boolean,
delay: number
},
monitor: {
enabled: boolean,
delay: number
}
},
lastStart: null | string,
lastStop: null | string,
license: {
acquired: boolean,
mode: "production"|"demo",
period: number,
tagAccess: "local"|"remote"
},
mode: null,
pid: null | number,
running: boolean
}
},
pid: number,
running: boolean,
ts: string,
uptime: string
}

www.vnodeautomation.com
I
39

$.api.module.getConfig

Function prototype:
$.api.module.getConfig<T>(moduleName: string): Promise<TagSourceConfiguration<T>>

This function is used to retrieve the configuration of the source tags belonging to a specific
module. It returns a promise that resolves to an object containing the configuration of the
source tags for the given module. It takes the following arguments:

o moduleName: Name of the module whose source tag configuration will be


retrieved.

Example:

const tagConfig = await $.api.module.getConfig("ModbusClient");

$.logger.info(JSON.stringify(tagConfig));

The following snippet is an example of the source tag configuration for a ModbusClient
module:

type TagSourceConfiguration<ModbusClient> = {
[tagPath: string]: {
type: "number"|"boolean"|"string",
link: null | string,
data: {
value: number|string|boolean|null,
quality: number,
ts: number
},
source: {
device: string,
address: string,
type: string,
rate: number
}
}
}

$.api.module.getConfigData

Function prototype: $.api.module.getConfigData(moduleName: null|string, configName:


string): Promise<ConfigData>

This function is used to retrieve a specific configuration from a module. It returns a promise
that resolves to the configuration of that module. The configuration is the raw
unprocessed configuration, it is the same as if reading the respective configuration file. It
takes two arguments:

www.vnodeautomation.com
I
40

• moduleName: Name of the instance whose configuration will be retrieved. If set to


null, it will retrieve a configuration from bootstrap (such as tags or links).

• configName: Name of the configuration that will be retrieved. This can be found by
using $.api.module.getConfigList.

Example:

const configData = await $.api.module.getConfigData("Historian", "logger");

$.logger.debug(JSON.stringify(configData));

$.api.module.getConfigList

Function prototype: $.api.module.getConfigList(moduleType: string):


Promise<ConfigList>

This function is used to retrieve the different configurations available to a module (such as
the default configuration, the logger configuration, etc...). It takes one argument:

o moduleType: Type of module whose configuration will be retrieved. This is


independent of the module's instance name, since it doesn't refer to a specific
instance (for example, ModbusClient can have an MC or Modbus instances, but the
type is ModbusClient).

Example:

const configData = await $.api.module.getConfigData("Historian", "logger");

$.logger.debug(JSON.stringify(configData));

The return value is a promise that resolves to the list of configurations available to the
specified module type. The structure of this object can be seen in the next type definition:

type ConfigList = {
[configName: string]: {
text: string,
icon: string
}
}

www.vnodeautomation.com
I
41

$.api.module.getConfigPresent

Function prototype: $.api.module.getConfigPresent(moduleName: string):


Promise<ConfigPresent>

This function is used to retrieve which configuration files are present (such as the default
configuration, the logger configuration, etc...). It takes one argument:

o moduleName: Name of the instance whose configuration will be checked.

Example:
const configPresent = await $.api.module.getConfigPresent("OpcUaClient");

$.logger.info(JSON.stringify(configPresent));

The return value is a promise that resolves to an object containing information about
which configurations are present. The structure of this object can be seen in the next type
definition:

type ConfigPresent = {
[configName: string]: boolean
}

$.api.module.getInstalled

Function prototype: $.api.module.getInstalled(): Promise<string[]>

This function is used to retrieve a list of the modules installed in the current node. It takes
no arguments and returns a promise that resolves to an array of strings containing the
name of the installed modules.

Example:

const modules = await $.api.module.getInstalled({});

for (const module of modules) {


$.logger.info(module);
}

www.vnodeautomation.com
I
42

$.api.module.setConfigData

Function prototype: $.api.module.setConfigData(moduleName: null|string, configName:


string, configData, options: {}): Promise<void>

This function is used to update the configuration of a module. It takes four arguments:

o moduleName: Name of the instance whose configuration will be updated. If null, it


affects configurations of bootstrap (such as tags or links).

o configName: Name of the configuration that will be updated. The various


configuration names for a module can be retrieved with
$.api.module.getConfigList.

o configData: New configuration that will be saved to disk for the specified module.
The configuration must be in a valid format, that is, it needs to follow the same
format as $.api.module.getConfig (in general, the easiest way to ensure the format
is correct is to retrieve the configuration and modify it).

o options:

▪ restart: Restarts module after saving new configuration (optional


parameter)

▪ deleteData: Only applicable to the module configuration file. List of deleted


module instances for cleaning their data. Can be a single instance, a
comma-separated list, or a JSON array (optional parameter).

Example:

const config =

'{"version":{"main":1,"editor":2},"result":["Object",{"bufferLimit":["Number",500000],"m
axDays":["Number",60],"db":["Object",{"run":["Boolean",false],"protocol":["String","mong
odb"],"host":["String","db-
mongo.internal"],"port":["Number",27017],"database":["String","history"],"parameters":["
String",""],"auth":["Object",{"enabled":["Boolean",false],"username":["String",""],"pass
word":["SecuredString",""]}],"tls":["Object",{"enabled":["Boolean",false],"ca":["String"
,""],"rejectUnauthorized":["Boolean",true]}]}]}],"editor":{"extends":{"module":null},"mo
dified":1711467825068,"comment":"","UID":0}}';

try {
await $.api.module.setConfigData("Historian", "default", config, {
restart: true,
});
} catch (error) {
$.logger.error(error);
}

www.vnodeautomation.com
I
43

$.api.backup

$.api.backup.create

Function prototype: $.api.backup.create(name: string, metadata: {user: string, description:


string}): Promise<void>

This function creates a new backup of the current node configuration. The newly created
backup will be saved to vNode/data/bootstrap/backup. It takes two arguments:

o name: Name of the backup that will be created.

o metadata.user: User that created the backup. This can be any string and doesn't
have to be an existing user.

o metadata.description: Description of the backup.

Example:

await $.api.backup.create("20240623-backup", {
user: "scripting",
description: "Automatic backup created with Scripting.",
});

$.api.backup.delete

Function prototype: $.api.backup.delete(name: string): Promise<void>

This function deletes a backup from the current node. It takes one argument:

o name: Name of the backup that will be deleted.

Example:

const backupName = "20240317-backup";

try {
await $.api.backup.delete(backupName);
$.logger.info("Backup deleted => %s", backupName);
} catch (error) {
$.logger.error("Failed to delete backup %s: %v", backupName, error);
}

www.vnodeautomation.com
I
44

$.api.backup.export

Function prototype: $.api.backup.export(name: string): Promise<string>

This function exports a backup from the current node. The return value is a promise that
resolves to a base64 string of the backup files's binary content (in .zip format). It takes one
argument:

o name: Name of the backup that will be exported.

Example:

const fs = require("fs");

const backupName = "20240317-backup";


const content = await $.api.backup.export(backupName);
const decodedContent = Buffer.from(content, "base64");

try {
fs.writeFileSync(
"/var/vNode/backups/" + backupName + ".zip",
decodedContent
);
$.logger.info("Backup exported => %s", backupName);
} catch (error) {
$.logger.error("Failed to export backup to disk: %v", error);
}

$.api.backup.get

Function prototype: $.api.backup.get(): Promise<Backups>

This function retrieves the list of backups present in the current node. The return value is
a promise that resolves to the following object:

type Backups = {
[name: string]: {
description: string,
user: string,
node: string,
ts: string,
version: {
modName: string]: string
}
}
}

www.vnodeautomation.com
I
45

Example:

const backups = await $.api.backup.get();

$.logger.info("Found %d backups", Object.keys(backups).length);

$.api.backup.import

Function prototype: $.api.backup.import(name: string, data: string): Promise<void>

This function imports a backup to the current node. It takes two arguments:

o name: Sets the name of the backup after it is imported.

o data: Base64 encoded data of a backup, either obtained through


$.api.backup.export, or by encoding a valid .zip backup file into base64.

Example:

const fs = require("fs");
const path = "/var/vNode/backups/20240404.1651.zip";

try {
const backupFile = fs.readFileSync(path);
const encodedData = Buffer.from(backupFile).toString("base64");
$.api.backup.import("test", encodedData);

$.logger.info("Backup imported => %s", path);


} catch (error) {
$.logger.error("Failed to import backup: %v", error);
}

$.api.backup.load

Function prototype: $.api.backup.load(name: string, options: {rollback: boolean}):


Promise<void>

This function applies the configuration of a backup to the current node. It takes two
arguments:

o name: Selects which backup will be loaded.

o options.rollback: When true, if the backup loading fails, the configuration will be
rolled back to the previous configuration. Otherwise, the backup loading process
proceeds, ignoring any errors that might result in invalid configuration for some
modules.

www.vnodeautomation.com
I
46

Example:

try {
await $.api.backup.load("20240404.1651", { rollback: true });
} catch (error) {
$.logger.error(error);
}

$.api.backup.rename

Function prototype: $.api.backup.rename(from: string, to: string): Promise<void>

This function renames the specified backup. It takes two arguments:

o from: The original name of the backup that will be renamed.

o to: The final name of the backup after being renamed.

Example:
await $.api.backup.rename("20240404.1651", "monthly-04");

$.api.redundancy

$.api.redundancy.status

Function prototype: $.api.redundancy.status(options: {}): Promise<RedundancyStatus>

This function is used to retrieve the status of the redundancy mode.

Example:
const status = await $.api.redundancy.status({});

$.logger.info(JSON.stringify(status));

$.api.redundancy.sync

Function prototype: $.api.redundancy.sync(paths: string[], absolute: bool): void

This function is used to synchronize files between the primary and backup nodes when
using redundancy. As such, it doesn't do anything if redundancy is not enabled. It takes
two arguments:

o paths: An array of paths that will be synchronized between the redundant nodes.

www.vnodeautomation.com
I
47

o absolute: This flag selects how the paths will be synchronized between the nodes.
If it's false, the paths will be relative to the vNode base folder (for example, when
synchronizing files in vNode/data between two vNode instances installed in
different paths). If it's true, the absolute path will be used so that files will be in the
same location in both nodes.

Example:

try {
await $.api.redundancy.sync(["data/ExampleModule"], false);
} catch (error) {
$.logger.error(error);
}

$.api.redundancy.unsync

Function prototype: $.api.redundancy.unsync(paths: string[], absolute: bool): void

This function is used to stop the synchronization of files between the primary and backup
nodes when using redundancy. As such, it doesn't do anything if redundancy is not
enabled. When executing $.api.redundancy.unsync, it will carry out one last
synchronization of the paths to synchronize their current state before stopping any further
synchronization. It takes two arguments:

o paths: An array of paths that will be synchronized between the redundant nodes.

o absolute: This flag selects how the paths will be synchronized between nodes. If it's
false, the paths will be relative to the vNode base folder (for example, when
synchronizing files in vNode/data between two vNode instances installed in
different paths). If it's true, the absolute path will be used, so the files will be in the
same location in both nodes.

Example

try {
await $.api.redundancy.unsync(["data/ExampleModule"], false);
} catch (error) {
$.logger.error(error);
}

www.vnodeautomation.com
I
48

$.api.tag

$.api.tag.browse

Function prototype: $.api.tag.browse(path: string, options:{hideTags?: boolean,


hideGroups?: boolean, recurrent?: boolean, flat?: boolean}):
Promise<BrowseResultObject|Array<string>>

This function lists the tags and groups present in a given path, and optionally, in all of the
sublevels of that path. It takes two arguments:

o path: Select which tag group to browse. To browse the root path, this argument
should be ‘/’. The path must end with a trailing ‘/’.

o options.hideTags: If true, tags are excluded from the result.

o options.hideGroups: If true, groups are excluded from the result.

o options.recurrent: If true, the result will also include tags and groups in the sub-
levels of the given path.

o options.flat: If true, returns an array with the full paths of all the tags at the given
paths (and in sub-levels if recurrent is set). This option only returns tags. It does not
return groups and as such, it cannot be used to list the groups in a given path.

Example:

const result = await $.api.tag.browse("/", {


hideTags: false,
hideGroups: false,
recurrent: true,
flat: false,
});

$.logger.info(JSON.stringify(result));

The return value of this function is a promise that can resolve to either an array of strings
(if options.flat is set) or the following object:

type BrowseResultObject = {
tags: Array<string>,
groups: {
[name: string]: BrowseResultObject
}
}

www.vnodeautomation.com
I
49

$.api.tag.read

Function prototype: $.api.tag.read(path: string, {recurrent: boolean, filter: string}):


Promise<TagData|GroupReadResult>
This function reads the value of one or several tags present at the given path. It takes two
arguments:
• path: Select the tag or group that will be read. If the path is a group, it has to end in
‘*’.
• options.recurrent: If true the values of the tags in the sub-levels of the given group
will also be read. This only applies when the path is a group.
• options.filter: When present, only tags that match the filter will be read. This is a
string that will be converted to a regular expression with the “ignore case” flag set.

Example:

const tag = await $.api.tag.read("/SITE_01/Temperature", {


recurrent: false,
});

$.logger.info(JSON.stringify(tag));

The return value of this function is a promise that resolves to one of the following
objects, depending on the arguments:

• If the path refers to a tag, it resolves to TagData.


• If the path refers to a group, it resolves to GroupReadResult.

type TagData = {
data: {
value: null|number|string|boolean,
quality: number,
ts: number
},
status: {
value: 0|1,
ts: number
}
}

type GroupReadResult = {
groups: {
[name: string]: GroupReadResult
},
tags: {
[name: string]: TagData
}
}

www.vnodeautomation.com
I
50

$.api.tag.details
Function prototype: $.api.tag.details<T>(path: string, {recurrent: boolean, filter: string}):
Promise<TagDetails<T>|GroupDetailsResult<T>>
This function retrieves details about a tag or tags present in a tag group. It takes two
arguments:
o path: Selects the tag or group that will be queried for details. If the path is a group,
it must end in ‘*’.
o options.recurrent: If true, the details of the tags in the sub-levels of the given group
will also be retrieved. This only applies when the path is a group.
o options.filter: When present, only tags that match the filter will be queried. This is
a string that will be converted to a regular expression with the "ignore case" flag
set.

Example:
const details = await $.api.tag.details("/*", {
recurrent: true,
filter: "Turbine",
});

$.logger.info(JSON.stringify(details));

The return value of this function is a promise that resolves to one of the following objects,
depending on the arguments:
o If the path refers to a group, it resolves to GroupDetailsResult<T>.
o If the path refers to a tag, it resolves to TagDetails<T>.

type GroupDetailsResult<T> = {
groups: {
[name: string]: GroupDetailsResult<T>
},
tags: {
[name: string]: TagDetails<T>
}
}

www.vnodeautomation.com
I
51

type TagDetails<T> = {
fullPath: string,
remote: null|string,
status: {
value: 0|1,
ts: number
},
data: {
value: null|number|string|boolean,
quality: number,
ts: number
},
config: {
clientAccess: "R"|"RW",
deadband: "string",
default: string,
description: string,
engUnits: string,
format: null|string,
persistency: 0|1|2,
security: {},
simulation: {
enabled: boolean
},
type: "number|string|boolean",
views: Array<string>,
extensions: {
ae: {
alarms: {
[name: string]: {
enabled: boolean,
description: string,
limit: number,
priority: 1|2|3|4,
mode: "ge"|"gt"|"le"|"lt"|"eq"|"ne"
}
}
},
history: {
enabled: boolean,
module: string,
config: {
deadband: string,
defaultMethod: "first"|"last"|"min"|"max"|"avg",
interpolation: "linear"|"step",
mode: "change",
rangeY: [min: number, max: number],
rate: [min: number, max: number]
}
},
scaling: {
enabled: boolean,
raw: [min: number, max: number],
eu: [min: number, max: number],
clamp: [min: boolean, max: boolean]
},
source: {
enabled: boolean,
module: string,
type: string,
config: TagConfiguration<T>
}
}
}
}

www.vnodeautomation.com
I
52

$.api.tag.history
Function prototype: $.api.tag.history(tag: string, start: number, end: number, options:
{mode: "raw"|"aggregated"|"filter"|"delta", method?: null|"first"|"last"|"min"|"max"|"avg",
interval?: number, deadband?: number}): TagHistory

This function is used to query historical data from a tag. It takes four arguments:
o tag: Selects the tag that will be queried for historical data.
o start: Selects the start date of the query. This is a date in UNIX Epoch format with
milliseconds.
o end: Selects the end date of the query. This is a date in UNIX Epoch format with
milliseconds.
o options.mode: Selects how the data is retrieved. More information about the
different ways of retrieving data can be found in vNode Historian’s User Manual.
o options.method: Selects the aggregation method that is used when retrieving the
data (only applies when the mode is "aggregated"). If set to null, it uses the method
defined in the tag configuration. More information about the different aggregation
methods can be found in vNode Historian’s User Manual.
o options.interval: Selects the aggregation interval, displayed in milliseconds (only
applies when the mode is "aggregated").
o options.deadband: Selects the deadband used when retrieving the data (only
applies when the mode is "delta" or "filter").
o options.invalidAsNull: Converts non-good quality values to null when using raw
mode (optional parameter, by default it is enabled).
o options.limit: Maximum number of samples to return when using raw mode
(optional parameter)

Example:

const history = await $.api.tag.history("/PLANT_01/ACTIVE_POWER", 1642214400000,


Date.now(), {
mode: "raw",
});

$.logger.info(JSON.stringify(history));

The return value of the function is a promise that resolves to the following object:

www.vnodeautomation.com
I
53

type TagHistory = {
request: {
tag: string,
start: number,
end: number,
options: {
remoteNode?: true|string|string[],
mode: "aggregated"|"raw"|"filter"|"delta",
method?: "first"|"last"|"min"|"max"|"avg",
interval?: number,
deadband?: number
},
data: Array<[ts: number, value: null|number|string|boolean]>
}
}

TagHistory.request is a copy of the arguments that were passed when executing the
historical request.

$.api.tag.write

Function prototype: $.api.tag.write(tag: string, value: null|number|string|boolean, options:


{}): Promise<void>

This function is used to write a value to a tag. It takes three arguments:

o tag: Selects the tag that will be written.

o value: Selects the value that will be written to the tag. The type must match the tag
type, otherwise, the write may fail or the written value might not be correct.

o options: Empty options object.

Example:

await $.api.tag.write('/SITE02/Speed', 200, {});

$.api.tag.getViews

Function prototype: $.api.tag.getViews(options: {}): Promise<Array<string>>

This function retrieves the views available in the current node. It takes one argument:

o options: Empty options object.

The return value of this function is a promise that resolves to an array of strings containing
the views available in the current node.

www.vnodeautomation.com
I
54

Example:

const views = await $.api.tag.getViews({});

$.logger.info("Found %d views.", views.length);

$.api.tag.update

Function prototype: $.api.tag.update(tag: string, value: null|number|string|boolean,


quality?: number, ts?: number, force?: boolean): void

This function is used to generate an update event on a Scripting source tag using its tag
path (it cannot be used to update tags whose source is not Scripting). It takes five
arguments:

o tag: Sets the path of the tag that will be updated. If the path is invalid or the tag is
not a Scripting source tag, a warning will be logged and the tag will not be updated.

o value: Sets the value of the tag update event. The type depends on the type of the
tag and if the types do not match, it is implicitly converted, if possible. This is not
recommended, since implicit conversion is not always valid ("hello world" can't be
converted to a number) and can result in invalid values.

o quality: Sets the quality of the tag update event. This value is optional and if not
provided, the tag will be automatically updated with good quality (192). vNode uses
an enum for the different qualities that can be found at $.qualityCodes. However,
any number in the 0-255 range is valid.

o ts: Sets the timestamp of the tag update event. The format is a number in UNIX
Epoch with milliseconds. This value is optional and if not provided, the event
timestamp will be set to the current time.

o force: If set, the function will always generate a new tag event, even if the value or
quality does not change. Otherwise, a tag event will only be generated if the value
or quality changes.

Example:

$.api.tag.update("/SITE02/ComputedByScripting", 5682, 48);

www.vnodeautomation.com
I
55

$.api.tag.updateAlias

Function prototype: $.api.tag.updateAlias(alias: string, value: null|number|string|boolean,


quality?: number, ts?: number, force?: boolean): void

This function is used to generate an update event on one or various Scripting source tags
using its tag alias (since multiple tags can have the same alias, it can be used to update
multiple tags at once with the same value). It takes five arguments:

o alias: Selects the alias that will be used to generate tag events. All of the tags with
matching alias will be updated. If the alias is non-existent, a warning will be logged
and the tag will not be updated.

o value: Sets the value of the tag update event. The type depends on the tag type
and if the types do not match, it is implicitly converted, if possible. This is not
recommended, since implicit conversion is not always valid ("hello world" can't be
converted to a number) and can result in invalid values.

o quality: Sets the quality of the tag update event. This value is optional and if not
provided, the tag will be automatically updated with good quality (192). vNode uses
an enum for the different qualities that can be found at $.qualityCodes. However,
any number in the 0-255 range is valid.

o ts: Sets the timestamp of the tag update event. The format is a number in UNIX
Epoch with milliseconds. This value is optional and if not provided, the event
timestamp will be set to the current time.

o force: If set, the function will always generate a new tag event, even if the value or
quality does not change. Otherwise, a tag event willonly be generated if the value
or quality changes.

Example:

$.api.tag.updateAlias("Speed", 82, 192););

$.api.tag.browseSource

Function prototype: $.api.tag.browseSource(path: string, {recurrent: boolean}):


Promise<Array<string>>

This function returns all the Scripting source tags present in the given path. It takes two
arguments:

o path: Selects the path that will be browsed. This path must start and end with a “/”.
Otherwise, the browse will fail.

o options.recurrent: When set, it will return tags from the given group, as well as any
sub-levels that are present. Otherwise, only tags present in the given group will be
returned.

www.vnodeautomation.com
I
56

This function returns a promise that resolves to an array of tag paths for all the tags that
were browsed.

Example:

const tags = await $.api.tag.browseSource("/", { recurrent: true });

for (const tag of tags) {


$.logger.info(tag);
}

$.api.tag.browseAlias

Function prototype: $.api.tag.browseAlias (): Promise<TagAliasList>

This function is used to retrieve all of the tag aliases and the tags that they refer to that are
present in the tag models' Scripting source tags. It takes no arguments and returns a
promise that resolves to the following object:

type TagAliasList = {
[alias: string]: Array<string>
}

Example:

const aliases = await $.api.tag.browseAlias();

$.logger.info(JSON.stringify(aliases));

$.api.tag.subscribe

Function prototype: $.api.tag.subscribe(tags: Array<string>, handler: (tag: string, data:


{value: number|string|boolean|null, quality: number, ts: number}, initial: boolean) => void):
symbol

This function is used to subscribe to tag events from a list of tags. It takes three arguments:

o tags: Selects tags that are part of the subscription. If only one tag is needed, an
array of one element must be used.

o Handler: Sets the callback that will be called whenever the given tags emit an
update event. The callback will be called with the following arguments:

▪ tag: Path of the tag that generated the update event.

▪ data.value: Contains the tag value of the event.

www.vnodeautomation.com
I
57

▪ data.quality: Contains the tag quality of the event.

▪ data.ts: Contains the tag timestamp of the event, displayed as a number in


UNIX Epoch format with milliseconds.

▪ initial: If true, it indicates that the event was received in response to a new
subscription (such as when the tag model changes). Otherwise, it's a normal
event.

This function returns a symbol, which can then be used to cancel the subscription by
calling $.api.tag.unsubscribe using the returned symbol.

Example:

const sub = await $.api.tag.subscribe(["/SITE_02/Wind_Speed"], onTagEvent);

function onTagEvent(tag, data) {


$.logger.info(JSON.stringify(data));
}

$.api.tag.unsubscribe

Function prototype: $.api.tag.unsubscribe(subscription: symbol): void

This function is used to cancel a previous subscription. It takes one argument:

o subscription: Selects which subscription will be cancelled based on the provided


symbol. This symbol must have been created by $.api.tag.subscribe.

Example:

$.api.tag.unsubscribe(sub);

$.api.alarm

$.api.alarm.get

Function prototype: $.api.alarm.get(path: string, options: {recurrent: boolean, filter: {path:


string, status: Array<number>, priority: number}}): Promise<Array<TagAlarmData>>

This function is used to retrieve the tag alarms present in a given path. It takes two
arguments:

www.vnodeautomation.com
I
58

o path: Sets the path that will be browsed for alarms. This must refer to a tag group.

o options.recurrent: When set, alarms for the given group, as well as any sub-groups,
will be retrieved. Otherwise, only alarms present in the given sub-group will be
returned.

o options.filter.path: Sets a regular expression that will be used to filter alarms based
on their full path. If the regular expression matches, the alarm will be shown in the
result.

o options.filter.status: Select which alarms will be shown in the result based on their
status. This can be set to null to retrieve all alarms independently of status, or it can
be an array of numbers selecting which alarms to retrieve based on the following
criteria: This is an array containing one or more of the following status codes:

• 0: Cleared and acked

• 1: Active and acked

• 2: Cleared and unacked

• 3: Active and unacked

o options.filter.priority: Selects the minimum alarm priority that will be shown in the
result. A value of null or 0 will retrieve all alarms. Otherwise, one of the following
priority codes can be used:

• 0: Low priority

• 1: Medium priority

• 2: High priority

• 3: Critical priority

Example:

const alarms = await $.api.alarm.get("/", {


recurrent: true,
filter: {},
priority: 2,
});

$.logger.info(JSON.stringify(alarms));

www.vnodeautomation.com
I
59

The return value of this function is a promise that resolves to an array of the following
object:

type TagAlarmInfo = {
path: string,
description: string,
priority: number,
remote: Nullable<string>,
online: 0|1,
status: 0|1,
ts: number,
value: tag.TagEvent,
format: Nullable<string>,
ackInfo: null | {
msg: string,
user: string,
options: {},
node: string,
module: string
}
}

$.api.alarm.count

Function prototype: $.api.alarm.count(path: string, options: {recurrent: boolean, filter:


{path: string, status: Array<number>, priority: number}}): Promise<number>

This function is used to retrieve the number of alarms present at a given path that match
a filter (such as number of active and unacked alarms). It takes two arguments:

o path: Sets the path that will be browsed for alarms. This must refer to a tag group.

o options.recurrent: When set, alarms for the given group, as well as any sub-groups,
will be retrieved. Otherwise, only alarms present in the given sub-group will be
returned.

o options.filter.path: Sets a regular expression that will be used to filter alarms based
on their full path. If the regular expression matches, the alarm will be shown in the
result.

o options.filter.status: Selects which alarms will be shown in the result based on their
status. This can be set to null to retrieve all alarms independently of status, or it can
be an array of numbers selecting which alarms to retrieve based on the following
criteria: This is an array containing one or more of the following status codes:

• 0: Cleared and acked

• 1: Active and acked

• 2: Cleared and unacked

• 3: Active and unacked

www.vnodeautomation.com
I
60

o options.filter.priority: Selects the minimum alarm priority that will be shown in the
result. A value of null or 0 will retrieve all alarms. Otherwise, one of the following
priority codes can be used:

• 0: Low priority

• 1: Medium priority

• 2: High priority

• 3: Critical priority

The return value of this function is a promise that resolves to the number of alarms that
match the filter.
const count = await $.api.alarm.count("/", {
recurrent: true,
filter: {},
priority: 0,
});

$.logger.warn("Found %d active alarms", count);

$.api.alarm.history

Function prototype: $.api.alarm.history(path: string, start: number, end: number, options:


{recurrent: boolean, filter: {path: string, status: Array<number>, priority: number}}):
Promise<TagAlarmHistory>

This function is used to query historical alarm data. It takes four arguments:

o path: Sets the path that will be browsed for alarms. This must refer to a tag group.

o start: Selects the start date of the query. This is a date in UNIX Epoch format with
milliseconds.

o end: Selects the end date of the query. This is a date in UNIX Epoch format with
milliseconds.

o options.recurrent: When set, alarms for the given group, as well as any sub-groups,
will be retrieved. Otherwise, only alarms present in the given sub-group will be
returned.

o options.filter.path: Sets a regular expression that will be used to filter alarms based
on their full path. If the regular expression matches, the alarm will be shown in the
result.

o options.filter.status: Selects which alarms will be shown in the result based on their
status. This can be set to null to retrieve all alarms independently of status, or it can
be an array of numbers selecting which alarms to retrieve based on the following
criteria: This is an array containing one or more of the following status codes:

www.vnodeautomation.com
I
61

• 0: Cleared and acked

• 1: Active and acked

• 2: Cleared and unacked

• 3: Active and unacked

o options.filter.priority: Selects the minimum alarm priority that will be shown in the
result. A value of null or 0 will retrieve all alarms. Otherwise, one of the following
priority codes can be used:

• 0: Low priority

• 1: Medium priority

• 2: High priority

• 3: Critical priority

Example:

const history = await $.api.alarm.history(


"/FAKE001",
1712422694,
1712522694,
{
recurrent: true,
filter: { priority: 3 },
}
);

$.logger.info(JSON.stringify(history));

The return value of this function is a promise that resolves to the following object (where
request is a copy of the historical request):

www.vnodeautomation.com
I
62

type TagAlarmData = {
path: string,
description: string,
priority: number,
remote: Nullable<string>,
online: 0|1,
status: 0|1,
ts: number,
value: tag.TagEvent,
format: Nullable<string>,
ackInfo: null | {
msg: string,
user: string,
options: {},
node: string,
module: string
}
};
type TagAlarmHistory = {
request: {
path: string,
start: number,
end: number,
options: {
recurrent?: boolean,
filter: {
path?: string,
status?: Nullable<Array<number>>,
priority?: Nullable<number>
}
}
},
data: Array<TagAlarmData>
};

$.api.alarm.ack

Function prototype: $.api.alarm.ack(paths: string|Array<string>, msg: string, user: string,


options: {}): Promise<void>

This function is used to acknowledge one or multiple alarms with a message and the user
that executed the acknowledgement. It takes three arguments:

o paths: Selects which alarm will be acknowledged. It can be either a single alarm, or
a list of alarms. The path must be a path to the specific alarm that will be
acknowledged, not a path for the tag that declares the alarm (alarm paths are
formed of TAG_PATH.ALARM_NAME). For example, if the tag
/C01/TemperatureAlarm has a “HiHi" alarm, the path of the alarm would be
/C01/TemperaturaAlarm.HiHi

o msg: Sets the acknowledgment message for the specified alarm.

o (String) user: User who acknowledged the alarm.

o (Function) callback (err): This callback will be called once the acknowledgement
request finishes. It has the following arguments:

▪ (String) err: Error message if the operation fails, or null if it succeeds.

www.vnodeautomation.com
I
63

Example:

await $.api.alarm.ack("/Site01/Temperature.TestAlarm", "Example message", "user", {});

$.qualityCodes

This enumeration provides different OPC quality codes that can be used to compare
against the quality value of a tag. The different qualities, along with their values are:

$.qualityCodes = {

BAD_NON_SPECIFIC: 0x00, //0


BAD_CONFIG_ERR0R: 0x04, //4
BAD_NOT_CONNECTED: 0x08, //8
BAD_DEVICE_FAILURE: 0x0C, //12
BAD_SENSOR_FAILURE: 0x10, //16
BAD_LAST_KNOWN_VALUE: 0x14, //20

BAD_COMM_FAILURE: 0x18, //24


BAD_OUT_OF_SERVICE: 0x1C, //28
BAD_UNINITIALIZED: 0x20, //32

UNCERTAIN_NON_SPECIFIC: 0x40, //64


UNCERTAIN_LAST_USABLE_VALUE: 0x44, //68

UNCERTAIN_SENSOR_NOT_ACCURATE: 0x50, //80


UNCERTAIN_EU_UNITS_EXCEEDED: 0x54, //84
UNCERTAIN_SUB_NORMAL: 0x58, //88

GOOD_NON_SPECIFIC: 0xC0, //192


GOOD_LOCAL_OVERRIDE: 0xD8 //216
}

www.vnodeautomation.com
I
64

cron library

cron is an internal vNode library used for both validation of cron syntax, as well as parsing
and retrieving the next (or several future) time(s) when the expression must execute.
When retrieving this library, an object will be returned with two keys; one which acts as
cron expression validation and the other as a scheduler.

const cron = $.lib("cron");

//cron has the following format:


type cron = {
validate: function(string),
schedule: typeof cron-schedule
}

validate(expression: string): Result<void, string[]>

The validate function is used to validate a cron expression. This function is a simplified
version of cron-validate, where it only takes a single string argument (no options required).
The return type is the same as cron-validate since it returns a Result-type. An example of
the validate function can be seen in the below code snippet:

const cron = $.lib("cron");

const expression = "* * * * *";


const result = cron.validate(expression);
if(result.isError()){
$.logger.warn("Invalid cron expression");
for(const errMsg of result.getError()){
$.logger.warn(errMsg);
}
}

Schedule

The schedule key contains the default export for the cron-schedule library. As such, any
code that is valid with that library is equivalent to using cron.schedule. The next code
snippet shows an example of how to get the next value assigned a cron expression:

const cron = $.lib("cron");

const expression = "0,15,30,45 * * * *";


const schedule = cron.schedule.parseCronExpression(expression);
const nextDate = schedule.getNextDate();
$.logger.debug(nextDate);

www.vnodeautomation.com
I
65

xlsxwriter library

xlsxwriter is an internal vNode library that can be used for basic data ingestion to Microsoft
Excel XLSX files. The library exports the following functions and classes:

Workbook

The workbook class represents an XLSX workbook, which is composed of one or several
sheets. This class can't be directly constructed. Instead, a new Workbook is created using
the static asynchronous Open method. Workbooks have the following methods:

static Open(path: string): Promise<Workbook>

This static method is used to open a new XLSX workbook at a given path.

getSheet(id: string|index): Promise<Worksheet>

This method is used to retrieve a worksheet from the current workbook. In order to select
which worksheet will be retrieved, a worksheet id must be provided. This can be either a
number, in which case it's the index of the worksheet, or a string corresponding to the
name of the worksheet.

setCalculateFlag(): Promise<void>

This method sets the "calculate on open" flag on the workbook. When set, the next time
the workbook is opened in Microsoft Excel, all of the cells of the workbook are recalculated.
If not set, the cells will keep their current cached values. If a cell is affected by a
modification using this library (whether it's directly affected or one of its dependencies is
affected), this flag must be set, otherwise the modification won't be propagated.

save(path: string): Promise<void>

This method saves the workbook to the given path.

Worksheet

The worksheet class represents a worksheet in the current workbook. This class can't be
directly constructed. Instead, an instance of it is created by the getSheet method of the
workbook. It has the following methods:

writeData(data: any[][], startRow: number, startColumn: number): Promise<Worksheet>

This method writes new data to the worksheet, overwriting any existing data. It takes the
following arguments:

• data: Data that will be written to the worksheet as an array of rows, where each row
is also an array.

• startRow: Selects which column will be written as the first row of the data. The data
will write rows, starting from startRow up to the length of each row in data.

• startColumn: Selects which column will be written as the first column of the data.
The data will write columns starting from startColumn until it has written all the
columns in data.

www.vnodeautomation.com
I
66

appendData(data: any[][], startRow: number, startColumn: number): Promise<Worksheet>

This method appends new data to the worksheet, keeping any existing data if the cells
already contain data. It takes the same arguments as writeData.

Installing NPM libraries

Scripting supports the use of any third-party libraries with the following limitations:

• The library must be compatible with node 12.22.12

• Native libraries can or cannot work depending on how they interact with the Node.js
runtime. In general, N-API libraries and NAN libraries that are declared as “context
aware” should work, but it depends on the implementation of each library.

Node.js version 12.22.12 must first be installed first on the host machine in order to install a
new third-party library and all of its dependencies from the NPM using just one single
command. The following sections show how to install Node.js 12.22.12, as well as how to
install NPM libraries.

Installing Node.js 12.22.12

• Download the installer from https://nodejs.org/dist/v12.22.12/. There are two versions for
Windows, one for 32-bit (node-v12.22.12-x86.msi), and one for 64-bit OS, located in the x64
folder(node- v12.22.12-x64.msi). The recommended one is the 64-bit version.

• Execute the installer by double clicking on it.

Figure 8. Installing Node.js

www.vnodeautomation.com
I
67

Click next until the Custom Setup window is opened. Include NPM package manager in
the features to install. It is also advisable to add Node.js to PATH:

Figure 9. Installing Node.js

Keep clicking next until Node.js finishes installation. It will then be possible to test if that
node has been successfully installed by opening a command prompt, and running the
following command to get the Node.js version installed: node --version.

Figure 10. Verifying Node.js installation

www.vnodeautomation.com
I
68

Installing NPM libraries

In this example, the got library will be installed in the vNode node.

1. The first step is to search for the NPM got library, which in this case is located at
https://www.npmjs.com/package/got. Since the library must be compatible with Node
12.22.12, the package.json file of the library must be checked to see if it lists the minimum
compatible version of Node.js. This file can be found by navigating to the repository of the
library:

Figure 11. Library NPM page

2. In the repository, the package.json file can be checked to see if it lists the minimum
supported Node.js version. This property is not mandatory, so some libraries might not have
it, but since it's good practice to include it, most will have it.

www.vnodeautomation.com
I
69

Figure 12. Library GitHub page

3. Within this package.json file, the keyword “engines” can be found using the search function.
This keyword is used to indicate the minimum node version supported by the library,
although, as mentioned earlier, this field is not required. If this keyword search returns no
results, the only way to test whether the library is compatible with Node.js 12.22.12 is by trial
and error, i.e. by installing the library and trying to use it. The following screenshot shows that
the current version of the library requires a Node.js version higher than v.14.16, and therefore,
this library is not compatible with vNode.

www.vnodeautomation.com
I
70

Figure 13. Package.json file

4. Even though the current version is not compatible, an older version might be. Older versions
can be checked by checking the tags in the repository, until one of the older version is
compatible with vNode.

Figure 14. Switching library versions

5. Checking the package.json of the 11.8.3 version shows that this version is compatible with
vNode:

www.vnodeautomation.com
I
71

Figure 15. Node.js 12.22.10 compatible version

6. In order to install the library, a command prompt in the destination folder (usually
vNode/data/Scripting) must be opened. Once it's open, the following command installs
the specific library and version:

npm install LIBRARY_NAME@LIBRARY_VERSION

In this example, the following command must be used:

npm install [email protected]

www.vnodeautomation.com
I
72

Figure 16. Node.js 12.22.10 compatible version

After executing this command, the got library will be install to the node_modules folder
(which is created if it doesn't exist) in the Scripting data folder. In order to load the library,
a new external library must be created, and it's path has to point to node_modules/got.

www.vnodeautomation.com
I
73

Examples
Inserting tag values to a MySQL database

Scripting can be used to connect to MySQL by using the mysql internal library. More
information about internal libraries can be found at Libraries.

The following example shows how to insert each new value of a tag into one of the tables
in the database.

This assumes a database and a table are already created with the following schema:

Figure 17. MySQL table schema

In order to insert the events, two scripts will be created, one serving as wrapper over the
raw MySQL connection (as a static library), and one startup script that will subscribe to
events and export them to the database.

The following libraries configuration is used:

Figure 18. MySQL and MyQLConnector libraries

www.vnodeautomation.com
I
74

The mysql connector static library contains the following script:

//Import the MySQL internal library


const mysql = $.lib("mysql");

//Export the MySqlConnection class


module.exports = class MySqlConnection{
//Constructor of the class taking a MySQL client instance
constructor(client){
this._client = client;
//Error event handler that logs a message to the logger
client.on("error", (err) => {
$.logger.warn("Error on client: %s (%d - %s)", err.message, err.errno,
err.sqlMessage);
//Additionally, if the error is fatal, the client is dropped
if(err.fatal){
this._client = null;
}
});
}

//This function must be called when the client is no longer being used
//in order to terminate the connection
dispose(){
return new Promise((resolve, reject) => {
if(this._client){
//If the client is online, end the client
this._client.end((err) => {
if(err){
reject(err);
} else {
resolve();
}
this._client = null;
})
} else {
reject(new Error("Connection already terminated"));
}
});
}

//Checks whether the client is online or not


online(){
return this._client !== null;
}

*Continues in the next page

www.vnodeautomation.com
I
75

*Starts in the previous page

//This function executes a query and resolve to a promise


//containing a tuple with the first element being the result
//of the query, and the second element the fields returned from
//the query
query(query, ...params){
return new Promise((resolve, reject) => {
if(this._client){
this._client.query(query, params, (err, result, fields) => {
if(err){
//If the error is fatal, set the client to null
if(err.fatal){
this._client = null;
}
reject(err)
} else {
resolve([result, fields]);
}
});
} else {
reject(new Error("Connection is terminated"));
}
});

}
//Static method used to asynchronously create a new client, which is necessary
since creating a connection
//is an asynchronous operation
static Connect(connOpts){
return new Promise((resolve, reject) => {
//Create the client with the provided options
const client = mysql.createConnection(connOpts);
//Connect the client to the DB
client.connect(err => {
if(err){
reject(err);
} else {
//If the connection is OK, create a MySqlConnection instance using the
client
//and resolve the promise with that instance
const connection = new MySqlConnection(client);
resolve(connection);
}
});
});
}
}

www.vnodeautomation.com
I
76

The startup script that relies on the library and is responsible for sending the event uses
the following code:

const MySqlConnection = $.lib("mysql_connector");


//Define the options used to connect to MySQL
const OPTIONS = {
host: "localhost",
port: 3306,
user: "admin",
password: "vnode",
database: "scripting"
};
//Define insertion period
const PERIOD = 10000;
//Define the tags that will be inserted into the DB
const TAGS = [
"/Simulated/String",
"/Simulated/Bool",
"/Simulated/Number"
];

//Subscribe to the tags


const subscription = await $.api.tag.subscribe(TAGS, onTagEvent);
//Buffer that holds the events between insertions
const events = [];
//Database client
let client = null;

//Set a 10 seconds timer before pushong the events


setTimeout(pushEvents, PERIOD);

//Handler that gets called whenever the tags have a new event
function onTagEvent(tag, data){
//Save the event to the buffer
events.push({tag, value: data.value, quality: data.quality, ts: data.ts});
}

//Async function called every PERIOD milliseconds to insert data into the DB
async function pushEvents(){
//If the client is null or offline, connect the client
if(!client || (client && !client.online())){
try{
await connect();
} catch (ex){
//If the connection fails, schedule a new timer
$.logger.warn("Error connecting: %s", ex.message);
return setTimeout(pushEvents, PERIOD);
}
}

*Continues in the next page

www.vnodeautomation.com
I
77

*Starts in the previous page

//If the client is connected, remove the events from the buffer
//and transform them into a format that can be used by MySQL
const ev = events.splice(0).map(el => {
//Set number_ev, string_ev and bool_ev either to the event
//or to null if the type does not match
const number_ev = (typeof el.value === "number")?el.value:null;
const string_ev = (typeof el.value === "string")?el.value:null;
const bool_ev = (typeof el.value === "boolean")?el.value:null;
//Return a 6 element tuple representing the values that will be inserted into
the DB
return [el.tag, number_ev, string_ev, bool_ev, el.quality, new Date(el.ts)];
});
try{
$.logger.debug("Inserting %d events", ev.length);
//Execute an INSERT query using ? as placeholder for the values
await client.query(`INSERT into tag_events (tag, number_value, string_value,
bool_value, quality, ts) VALUES ?`, ev);
$.logger.debug("Inserted OK", ev.length);
} catch (ex){
$.logger.warn("Error during query: %s", ex.message);
//If the query fails and the client is offline, null the client
if(!client.online()){
client = null;
}
} finally{
//After finishing the query (whether it was successful or not)
//schedule a new timer
setTimeout(pushEvents, PERIOD);
}
}

//Creates a new MySQL client


async function connect(){
$.logger.debug("Connecting to MySQL using %s@%s:%d", OPTIONS.user, OPTIONS.host,
OPTIONS.port);
client = await MySqlConnection.Connect(OPTIONS);
$.logger.debug("Connected OK");
}

www.vnodeautomation.com
I
78

Reading tag values from Microsoft SQLServer database

This example reads data from a Microsoft SQL Server database and saves it to Scripting
source tags. This is done using the mssql NPM library, which is integrated in vNode (and
thus can be used as an internal library).

This example assumes the target table has the following schema, and that it's data it's being
updated from an external source:

Figure 19. Microsoft SQL Server table schema

In this example, the table has the following data:

Figure 20. Data sources used in this example

This data will be saved to Scripting source tags using an alias, this allows the tags in vNode
to have a different structure to those in Microsoft SQL Server. A configuration of a source tag
can be seen in the next screenshot:

www.vnodeautomation.com
I
79

Figure 21. Configuration of source tags

This example will use two scripts (a periodic script and a static library) as well as an internal
library. The following two libraries are used:

Figure 22. Microsoft SQL Server and mssql_connector libraries

www.vnodeautomation.com
I
80

The mssql_connector static library has the following script:

//Import the Microsft SQL Server internal library


const mssql = $.lib("mssql");

//Export the SqlServerConnection class


module.exports = class SqlServerConnection{
//Constructor of the class taking a SqlServerConnection client instance
constructor(client){
this._client = client;
//Error event handler that logs a message to the logger and sets the client to
null
client.on("error", (err) => {
$.logger.warn("Error on client: %s", err.message);
//Drop the client after an error
this._client = null;
});
}

//This function must be called when the client is no longer being used
//in order to terminate the connection
dispose(){
if(this._client){
this._client.close();
this._client = null;
} else {
throw new Error("Connection is already closed");
}
}
//Checks whether the client is online or not
online(){
return this._client !== null;
}

//This function executes a query and resolve to a promise


//containing a tuple with the first element being the result
//of the query, and the second element the fields returned from
//the query
async query(query, ...params){
if(this._client){
//Creates a new request on the connection, and executes the query
return await new mssql.Request(this._client).query(query);
} else {
throw new Error("Connection is offline");
}

}
//Static method used to asynchronously create a new client, which is necessary
since creating a connection is an asynchronous operation
static async Connect(connOpts){
//Create the client with the provided options and connect with DB
const client = await mssql.connect(connOpts);
//If the connection is OK, create a SqlServerConnection instance
//using the client and return it
return new SqlServerConnection(client);
}
}

www.vnodeautomation.com
I
81

The periodic script has a 10 second trigger, and it's used to retrieve data from the database
and save it to the source tags. It has the following code:

const SqlServerConnection = $.lib("mssql_connector");


//Define the options used to connect to Microsoft SQL Server
const OPTIONS = {
server: "10.1.3.169",
port: 1433,
user: "sa",
password: "vnode",
database: "scripting"
};

//If the local variables are not initialized, initialize them


if($.local.init === undefined){
await init();
}

//Retrieve the data, and exit the script after the async call is finish
await getData();
$.exit();

//Async function used to retrieve the data


async function getData(){
//If the client is not online, attempt to reconnect
if($.local.client === null || !$.local.client.online()){
const client = await connect();
//If the client connects OK, save it to $.local.client and keep going
if(client !== null){
$.local.client = client;
} else {
//If the client is null, it means the connection was unsuccessful, so the
query can't be executed
return;
}
}
try{
//Try to retrieve the data using a SELECT query
const queryResult = await $.local.client.query("SELECT tag, value FROM
dbo.data");
//For every row in the result, destructure the row object into the tag and value
variables
//and execute a tag alias update
for(const {tag, value} of queryResult){
$.api.tag.updateAlias(tag, value);
}
} catch (ex){
//If the query has an error, log the error
$.logger.warn("Error during query: %s", ex.message);
}
}

*Continues in the next page

www.vnodeautomation.com
I
82

*Starts in the previous page

//Creates and returns a new Microsoft SQL Server client


async function connect(){
$.logger.debug("Connecting to Microsoft SQL Server using %s@%s:%d", OPTIONS.user,
OPTIONS.server, OPTIONS.port);
try{
const client = await SqlServerConnection.Connect(OPTIONS);
$.logger.debug("Connected OK");
return client;
} catch (ex){
$.logger.warn("Error connecting client: %s", ex.message);
return null;
}
}

//Initializes the required local variables


async function init(){
$.local.init = true;
//The client must be saved in a local variable to only has one instance
//that persists between script executions
$.local.client = null;
}

Writing data to XLSX files

This example reads data from Historian, using the history API call, and stores it to a
template XLSX file that can be used for further data processing. This example will use the
following XLSX file:

The Processed Data worksheet contains an average function over all the rows (skipping
the first row, which are going to be used as the headers) of column B of _Data, while the
_Data worksheet is empty, and it will be automatically filled by vNode based on data from
Historian.

www.vnodeautomation.com
I
83

In order to have access to the XLSX writer library, an internal library instance must be
created:

Finally, the following script is used to retrieve events from Historian, and insert them as
well as the headers:

// Load the XLSX library


const Workbook = $.lib("xlsx");

const tag = "/Memory/Historical";

// Retrieve 1 hour of data


const end = Date.now();
const start = end - 3600000;
const history = await $.api.tag.history(tag, start, end, { mode: "raw" });

// Open the template workbook


const workbookTemplatePath = String.raw`C:\Dev\Manual.xlsx`;
$.logger.debug("Opening workbook...");
const workbook = await Workbook.Open(workbookTemplatePath);

// Retrieve the _Data worksheet


$.logger.debug("Workbook opened OK, retrieving worksheet...");
const sheet = await workbook.getSheet("_Data");
$.logger.debug("Worksheet retrieved OK, inserting data...");

// Write the headers in the first row


sheet.writeData([["Timestamp", tag]], 0, 0);

// Parse the data as necessary


const cellData = [];
for (const [ts, value] of history.data) {
cellData.push([ts, value]);
}

// Write the data, setting the startRow to 1 in order to not overwrite the headers
sheet.writeData(cellData, 1, 0);

// Save the workbook to a different path to avoid overwriting the template


const workbookOutPath = String.raw`C:\Dev\Manual_modified.xlsx`;
$.logger.debug("Data inserted OK, saving workbook...");
await workbook.save(workbookOutPath);
$.logger.debug("Workbook saved OK");

www.vnodeautomation.com

You might also like