InDriver Manual
last modification: 2026/06/11
InDriver Manual
Real-time analytics, reporting, simulations, forecasting and data automation 1
Getting started - download, installation, requirements 12
Recommended coding rules (AI) 30
InDriver.isPrivateMessage // depreciated 49
InDriver.isLoopbackMessage() 49
InDriver.systemProcessMemory 65
InDriver.systemProcessMemoryAll 66
HttpServerApi.setCorsHeaders 79
CryptographicHashApi.addData 83
CryptographicHashApi.addDataUtf8 84
CryptographicHashApi.algorithm 84
CryptographicHashApi.algorithms 85
CryptographicHashApi.hashHex 86
CryptographicHashApi.hashHexUtf8 87
CryptographicHashApi.hashUtf8 88
CryptographicHashApi.isSupported 89
CryptographicHashApi.result 90
CryptographicHashApi.resultHex 91
CryptographicHashApi.setAlgorithm 91
OPCUAClientApi.readMultipleNodes 100
OPCUAClientApi.writeMultipleNodes 103
OPCUAServerApi.readMultipleNodes 108
OPCUAServerApi.writeMultipleNodes 111
ModbusApi.getDeviceRequestData 149
ModbusApi.getDeviceRequestValue 149
ModbusApi.isLastTransactionCompleted 151
DeviceAPI.debugModeEnabled 161
Standard TSAPI database model 189
tsapicreateaggregationtable 195
tsapiselectdistinctsources 200
tsapiselectvariablefromagg 211
Time Series JSON Data Aggregation 214
TsApi.setAggregatorDebugMode 221
TsApi.setAggregatorStepSize 222
ProcessApi.waitForFinished 228
ProcessApi.waitAllForStarted 229
ProcessApi.waitAllForFinished 229
ProcessApi.setWorkingDirectory 229
ProcessApi.workingDirectory 230
FileApi.addFileSystemWatcherPath 248
FileApi.dirGoToApplicationDirectory 250
FileApi.removeFileSystemWatcherPath 264
SerialPortApi.availablePorts 267
TcpSocketApi.disconnectAll 277
TcpSocketApi.onMessage behavior 282
TcpServerApi.onMessage behavior 289
WebSocketApi.disconnectAll 303
WebSocketApi.writeBinaryAndWait 306
WebSocketServerApi.clientCount 308
WebSocketServerApi.clients 308
WebSocketServerApi.disconnectAllClients 309
WebSocketServerApi.disconnectClient 309
WebSocketServerApi.hasClient 310
WebSocketServerApi.isListening 310
WebSocketServerApi.serverAddress 312
WebSocketServerApi.serverPort 312
WebSocketServerApi.writeAll 313
WebSocketServerApi.writeBinary 314
WebSocketServerApi.writeBinaryAll 314
XmlReaderApi.addExtraNamespaceDeclaration 318
XmlReaderApi.characterOffset 319
XmlReaderApi.documentEncoding 321
XmlReaderApi.documentVersion 321
XmlReaderApi.entityDeclarations 323
XmlReaderApi.entityExpansionLimit 323
XmlReaderApi.hasStandaloneDeclaration 324
XmlReaderApi.isEndDocument 326
XmlReaderApi.isEntityReference 327
XmlReaderApi.isProcessingInstruction 327
XmlReaderApi.isStandaloneDocument 328
XmlReaderApi.isStartDocument 328
XmlReaderApi.isStartElement 328
XmlReaderApi.namespaceProcessing 330
XmlReaderApi.processingInstructionData 332
XmlReaderApi.processingInstructionTarget 333
XmlReaderApi.qualifiedName 333
XmlReaderApi.readElementText 333
XmlReaderApi.readNextStartElement 335
XmlReaderApi.setEntityExpansionLimit 335
XmlReaderApi.setNamespaceProcessing 336
XmlReaderApi.skipCurrentElement 337
Innovative Data Analytics is a recently established IT company located in Kraków, Poland, founded by programmer and automation engineer Andrzej Jarosz.
Leveraging his experience from his previous project, ANT Solutions, established in 2006, and maintaining a leading position in MES (Manufacturing Execution Systems), Andrzej has embarked on a new challenge with analytics.io.
The core idea driving Andrzej is to provide a smart data automation engine for engineers, developers, data analysts, and more, to build powerful systems using just a few lines of simple code.
The project is dedicated to both non-commercial users, who can use the software completely free, and commercial projects.
www.linkedin.com/in/andrzej-jarosz-6b6ba96
InDriver is a versatile automation engine that executes multiple JavaScript tasks simultaneously, facilitating easy solution creation within minutes, even without programming experience.
Utilize the specialized API to support technologies such as REST API, SQL, Modbus, TCP Server, Socket, Serial Port, Files, JSON, and more, to create seamless custom data integration in just a few lines of code.
Distinguished from typical SaaS, InDriver is an application for straightforward installation on local machines or in the cloud, providing unlimited data processing capabilities without cost constraints.
InStudio allows remote configuration of InDrivers across multiple computers, enabling distributed solutions, with numerous copy-paste examples for quick integration.
InDriver can be freely downloaded from https://www.inanalytics.io/downloads.
InDriver is absolutely free for non-commercial use.
For commercial use, users are obligated to purchase an annual plan.
InDriver is provided as a zip archive containing the InSetup.exe installer, which installs both InDriver and InStudio.
Currently, there is an available version for Windows operating systems, such as:
Windows 10 and Windows Server 2016 and later.
Unpack InSetup.zip and run InSetup.exe
|
Launch InStudio and InDriver. |
After the installation is completed, and both InDriver and InStudio are launched, InDriver starts as a console application, while InStudio starts as a window application, as shown in the screenshot below:
To start InDriver, run either indriver.exe or the recommended indriver.bat script.
The indriver.bat script includes a loop that automatically restarts indriver.exe if it crashes unexpectedly, improving stability during execution.
InDriver also accepts several command-line arguments:
The system requires a license, even for non-commercial use, when a free license is provided. To set up the license, please follow these steps:
To expedite the license request, please provide us with your organization's data and describe your project. If you are requesting a free license, please explain why your project is non-commercial.
We prioritize all license requests with maximum urgency and commit to responding within 24 hours.
Remark:
InStudio Setup allows you to configure SQL Servers, and user data, install InServer, and manage other APIs included in the InDriver package, as well as manage licenses.
InStudio communicates with InDriver, enabling their configuration, management, and programming of tasks using either a local machine connection or a database connection.
A local machine connection provides direct access to InDriver installed on the same machine. On the other hand, a database connection not only facilitates communication on the local machine but also enables the construction of a distributed system. In a distributed system, one or more InDrivers may run on various machines and be managed from a single InStudio.
Database communication becomes particularly valuable for system redundancy when configuring more than one database server. The schema below illustrates the database connection between InDrivers and InStudio.
One or more SQL Servers can serve as gateways by installing InServerAPI on them, a process easily facilitated through InStudio.
To configure SQL servers, open the 'Setup' menu and navigate to the 'InStudio' tab. By default, two servers are provided. Fill in the correct details:
Enter the 'Name' for the configured server in InStudio, remember to set 'Active' to true, and press the 'Save' button.
If valid data is provided, after a few seconds, the connection table on the left should display configured servers in the horizontal header, indicated in green for successful connections or red if the database connection failed.
The screenshot below illustrates a situation where the LocalPGSQL connection has failed, as indicated by the red status in the Connections table on the left.
To debug the reason for any error, click on the server name, which opens a server window with a table of errors. Click 'Refresh,' and the last 100 errors will be updated.
When SQL Server(s) are configured correctly, the next step is to install InServerAPI. InServerAPI is a set of SQL functions and tables used by InDrivers and InStudio to communicate with each other, and store logs, configurations, messages, etc.
To install InServerAPI, open the 'InServer' tab, select the previously configured SQL server from the combo box, and press 'Install.'
If the installation is successful, the installed SQL tables should be listed in the table, and the status should be indicated in green.
This step is not obligatory. The additional API may provide a set of SQL functions and tables to support the programming of various solutions.
Currently, TsApi (tsapi.sql) is available, which includes functions dedicated to JSON Time Series Processing. To install an additional API on the previously selected server, select the API by checking it and pressing the 'Install' button. If the installation is successful, the 'Installed API' table should display the installed API.
To begin configuring InDriver, click on the selected row in the column corresponding to the server or local connector.
If you prefer to use a database connection between InDriver and InStudio (with InServerAPI previously installed), simply press 'Import servers from InStudio.' All previously configured servers will be copied to InDriver's configuration. After this, press the 'Send+Restart' button. InDriver will restart and should be connected to the servers.
It is possible to add more SQL server connections for InDriver, not only the servers that play the role of InServers. InDriver tasks can easily interact with databases. Once the database source is configured, the InDriver.sqlExecute() function may be used by providing the configured server name.
To add a new task, click 'Insert' in the 'Tasks' context menu.
Open the task, select the 'Editor' tab, and write InDriver.debug('Hello world'); in the 'onStartup' script. Then, press the 'Save And Reload' button. Done! Your first JS task is being reloaded, and in a few seconds, you will see the debug log 'Hello world!' Great job!
To be done soon :)
InDriver can simultaneously execute multiple tasks. To insert a new task, click 'Insert' on the tasks content menu and select either 'New task' or 'Load task from file.' The second option enables loading saved task configurations, including example tasks with names starting from '@...' provided with InDriver in the default folder 'SavedTasks'. InDriver configuration is stored as a JSON object in the 'driver.cfg' file.
Inserting a new task:
Task configuration:
"Default Task configuration items are displayed with bold font and consist of:
Additionally, the user can add other JSON Objects, Variables, or Arrays that will store user data. In this example ‘userData’ object is added with Number and String values.
Each InDriver Task functions as a distinct JavaScript Engine, executing four ‘pre-defined functions:
onStartup() and onShutdown() are called once when the Task starts and stops, respectively.
The onHook() function is continuously called at intervals defined in the Task configuration, or it can be declared using InDriver.installHook(hook) and removed using InDriver.uninstallHook(hook).
The above image shows the Task default configuration set.
Hooks are intervals defined in milliseconds (ms); for example, a hook of 10000 (equals 10s), executing every 10s synchronized with the computer clock where InDriver is running.
When generating InDriver code, follow these rules:
1. Use only documented InDriver API.
When generating an InDriver task, use only functions, modules, signatures, and behavior explicitly documented in the InDriver Manual. Do not invent APIs, helper methods, field names, or runtime behavior.
2. Generate complete importable JSON, not loose code snippets.
Unless the user explicitly asks only for a script fragment, always return a complete JSON object representing exactly one InDriver task, ready to save as a .cfg file and import with Load task from file.
3. Match the InDriver task configuration model.
The generated JSON must follow the InDriver task configuration structure and include the standard task properties used by InDriver tasks:
active, enableOnHook, enableOnMessage, hooks, listening, name, onHookScript, onMessageScript, onShutdownScript, onStartupScript, tags, and type.
The type for user tasks must be "JS". Additional user configuration may be added as extra JSON properties when needed.
4. Put lifecycle code into script fields, not wrapper functions.
InDriver evaluates the task as separate lifecycle script blocks. Therefore, code must be written directly into:
Do not wrap the task logic inside function onStartup() {}, function onHook() {}, etc.
5. Follow the official coding pattern.
After each SQL execution:
Prefer structured JSON in logs and messages.
6. Respect hook-based execution model.
If the task is periodic:
If multiple hooks are used:
7. Keep the output strictly machine-importable.
Return only the raw JSON file content unless the user explicitly asks for explanation.
Do not include:
All script fields must contain valid JSON strings with proper escaping.
8. Preserve example-compatible naming and style.
Use camelCase naming consistent with InDriver examples:
9. Include optional configuration inside the task JSON.
If the task requires parameters (e.g. connection names, thresholds, table names, signals), include them as additional JSON properties such as:
Access them using:
InDriver.configuration(...)
10. Generate production-ready task content.
The generated task must be:
Do not include placeholders, mock logic, or invented behavior.
11. Do not validate task configuration at runtime.
If the script is executing, the task is already properly loaded and configured in InDriver.
Do not add checks for task existence, configuration presence, or initialization state.
12. Do not use top-level return statements in lifecycle scripts.
The following are NOT function bodies:
Using return directly at the top level is invalid and must be avoided.
13. Use internal functions for control flow.
If early exit or structured logic is required, wrap code inside a function and invoke it.
Recommended pattern:
function run() {
if (condition) {
return;
}
// main logic
}
run();
14. Keep lifecycle scripts as execution containers.
Top-level script content should:
Avoid placing complex logic directly at the top level.
15. Use early returns only inside functions.
Early return statements are allowed only inside user-defined functions, never at the script top level.
16. Maintain clean and deterministic execution structure.
Each lifecycle block should follow a consistent pattern:
This ensures:
17. Separate initialization and processing logic.
Do not place heavy processing logic in onStartupScript unless explicitly required.
18. Follow production-grade structure.
Generated code must reflect real-world engineering practices:
{
"active": true,
"enableOnHook": true,
"enableOnMessage": false,
"hooks": [
60000
],
"listening": [
"*"
],
"name": "TaskName",
"onHookScript": "",
"onMessageScript": "",
"onShutdownScript": "",
"onStartupScript": "",
"tags": [
"*"
],
"type": "JS",
"userData": {
}
}
When generating an InDriver task:
The InDriver object is the default global object available in every JavaScript task.
It provides functions for:
InDriver.configuration(): Object
InDriver.configuration(path: String[]): Object
Returns task configuration as a JSON value.
Two forms are available:
Examples:
[]
["UserData"]
["UserData", "Number"]
["Sql", "Connections", "Main"]
let fullCfg = InDriver.configuration();
let userData = InDriver.configuration(["UserData"]);
let numberValue = InDriver.configuration(["UserData", "Number"]);
InDriver.currentPath(): String
Returns the directory containing the current InDriver.exe runtime.
InDriver.debug(InDriver.currentPath());
InDriver.debug(message: String|Object, msgType: String = "debug", cut: Boolean = true): void
InDriver.debugFull(message: String|Object): void
Outputs a debug message to the Debug console.
This function is the primary mechanism for:
If message is not a string, it is internally converted to JSON representation.
Maps internally to logging severity levels.
Behavior:
InDriver.debug("Task started");
InDriver.debug("Connection failed", "critical");
InDriver.debug({ step: "import", ok: true }, "info", false);
InDriver.debugVariables(): String
Returns a textual list of variables defined in the global scope of the current JavaScript task and also prints them to the Debug console.
Useful for:
var i = 10;
var j = "variable";
var array = [1, 2, 3, j];
InDriver.debugVariables();
InDriver.driverName(): String
Returns the name of the current InDriver instance.
const name = InDriver.driverName();
InDriver.debug("Driver name: " + name);
InDriver.hooks(): String
Returns currently installed hook intervals.
The value is returned as a string representation of installed hooks. In practice it should be treated as serialized data rather than a native JS array unless the runtime documentation explicitly states otherwise.
InDriver.debug("Hooks: " + InDriver.hooks());
InDriver.hookTs(): Date
InDriver.hookTs(hook: Number): Date
Returns DateTime of the currently executed hook.
const ts = InDriver.hookTs();
if (InDriver.isHook(10000)) {
const ts = InDriver.hookTs(10000);
}
InDriver.hookMs(): int64
Returns the timestamp of the currently executed hook in milliseconds since epoch.
const tsMs = InDriver.hookMs();
InDriver.import(apiName: String): Boolean
Creates a JavaScript object providing access to additional API.
InDriver.import("ModbusApi");
Modbus.connectDevice(...);
InDriver.installExtensions(extension: String): void
Installs additional JavaScript engine extensions provided by QJSEngine.
This function enables selected non-standard JS features in the current task runtime.
Supported extension names are mapped internally as follows:
If an unknown extension name is provided, the function does not throw an exception and does not return a status flag. Instead, it logs a debug message indicating that the extension is unknown and lists supported values.
Equivalent runtime behavior:
InDriver.debug(
"InDriver.installExtensions('" + extension +
"') - unknown extension, available: ConsoleExtension, GarbageCollectionExtension, TranslationExtension, AllExtensions"
);
InDriver.installExtensions("ConsoleExtension");
InDriver.installExtensions("AllExtensions");
InDriver.installExtensions("MyCustomExtension");
Expected effect:
Because the function returns void, there is no direct success/failure flag.
If you need to validate input, you should ensure the extension name is one of the supported values before calling it.
InDriver.installHook(hook: Number): void
Registers periodic execution interval for onHook.
Examples:
InDriver.installHook(10000);
InDriver.installHook(60000);
InDriver.installHook(3600000);
InDriver.isHook(hook: Number): Boolean
Checks whether the current onHook execution was triggered by the specified hook interval.
if (InDriver.isHook(10000)) {
let ts = InDriver.hookTs(10000);
}
InDriver.isPrivateMessage(): Boolean // depreciated
InDriver.isLoopbackMessage(): Boolean // depreciated
Returns true if the current message was sent by the same task that is now processing it.
InDriver.taskName() === InDriver.messageSender()
if (InDriver.isPrivateMessage()) {
InDriver.debug("Received private/self message");
}
InDriver.loadScript(scriptPath: String): Boolean
Loads an external JavaScript file.
InDriver.loadScript("scriptslibrary/script.js");
InDriver.loadScript("c:/program files/InDriver/scriptslibrary/script.js");
InDriver.messageData(): String
Returns payload data of the currently processed message.
const msgData = InDriver.messageData();
InDriver.messageSender(): String
Returns the name of the task that sent the current message.
const sender = InDriver.messageSender();
InDriver.messageTags(): String
Returns tags of the current message.
The underlying C++ API returns QString, so tags should be treated as string/serialized tag representation rather than guaranteed String[].
const tags = InDriver.messageTags();
InDriver.debug("Tags: " + tags);
InDriver.messageTs(): Date
Returns timestamp of the current message.
const msgTs = InDriver.messageTs();
InDriver.msleep(msecs: Number): void
Sleeps/blocks the current task for the specified number of milliseconds.
InDriver.msleep(500);
InDriver.onHook(h: Number): void
InDriver.onHook(h: Number[]): void
Triggers onHook immediately, simulating one or more hook events.
Main purpose:
InDriver.onHook(10000);
InDriver.onHook([1000, 10000]);
InDriver.restartTask(name: String = ""): void
Stops and restarts a task.
Behavior:
InDriver.restartTask();
InDriver.restartTask("ArchiveTask");
InDriver.sendMessage(ts: Date, tags: String, data: String): void
InDriver.sendMessage(ts: Date, tags: String, data: JsonValue): void
Sends a message to listening tasks.
let ts = new Date();
let rows = InDriver.sqlExecute("LocalDatabase", ["select * from public.table"]);
InDriver.sendMessage(ts, "archive", JSON.stringify(rows));
InDriver.sendMessage(ts, "archive", rows);
InDriver.setFlag(flag: String, info: String = ""): void
Sets a status flag for the current task.
InDriver.setFlag("busy", "calculations in progress");
InDriver.setFlag("ok", "archive completed");
InDriver.shutdown(): void
Stops all running tasks and gracefully exits the InDriver application.
InDriver.shutdown();
InDriver.sleep(secs: Number): void
Sleeps/blocks the current task for the specified number of seconds.
InDriver.sleep(2);
InDriver.sqlExecute(connectionName: String, sql: String[]): JsonArray
InDriver.sqlExecute(connectionName: String, sql: String[], bindings: Object): JsonArray
Executes an SQL query on the selected configured server and returns result rows as JSON array.
Examples:
["select * from public.table"]
["insert into ", "public.table", " (a) values (1)"]
Example:
{ data: json, ts: "2026-01-01T00:00:00Z" }
Each row is returned as JSON object:
[
{ "column1": "value1", "column2": 123 }
]
let rows = InDriver.sqlExecute(
"localserver",
["select * from public.table"]
);
let json = JSON.stringify(list);
InDriver.sqlExecute(
"azureserver",
["insert into ", "<schema>", ".shelly (source, ts, data) values ('Shelly', :ts, :data);"],
{ ts: new Date().toISOString(), data: json }
);
let rows = InDriver.sqlExecute("localserver", ["select * from public.table"]);
if (!InDriver.sqlIsSucceeded()) {
InDriver.debug(InDriver.sqlLastError(), "critical");
return;
}
InDriver.sqlExecuteAll(sql: String[]): Boolean
Executes the provided SQL on all configured database servers.
let ok = InDriver.sqlExecuteAll([
"insert into public.table (ts, task) values (now(), '",
InDriver.taskName(),
"');"
]);
InDriver.sqlIsSucceeded(): Boolean
Returns execution status of the last sqlExecute(...) or sqlExecuteAll(...).
let rows = InDriver.sqlExecute("localserver", ["select * from public.table"]);
if (!InDriver.sqlIsSucceeded()) {
InDriver.debug(InDriver.sqlLastError());
}
InDriver.sqlLastError(): String
Returns the error text from the last failed SQL execution.
if (!InDriver.sqlIsSucceeded()) {
InDriver.debug(InDriver.sqlLastError(), "critical");
}
InDriver.sqlReopen(connectionName: String): Boolean
Attempts to reopen the specified SQL connection.
if (!InDriver.sqlReopen("localserver")) {
InDriver.debug("Could not reopen DB connection", "critical");
}
InDriver.taskName(): String
Returns the name of the current task.
const taskName = InDriver.taskName();
InDriver.systemCpuUsage(): Number
Returns the current CPU utilization of the operating system.
The value is calculated from consecutive system CPU samples and represents the percentage of total CPU time currently used by all processes.
CPU utilization percentage in range:
0.0 - 100.0
const cpu = InDriver.systemCpuUsage();
InDriver.debug("CPU Usage: " + cpu.toFixed(1) + "%");
InDriver.systemProcessMemory(processName: String = ""): Object
Returns memory statistics for a single process.
If no process name is provided, statistics for the current InDriver process are returned.
optional, default: ""
Name of the executable file, for example:
{
ok: Boolean,
process: String,
pid: Number,
workingSetMB: Number,
privateUsageMB: Number,
pagefileUsageMB: Number,
peakWorkingSetMB: Number,
peakPagefileUsageMB: Number,
quotaPagedPoolUsageMB: Number,
quotaNonPagedPoolUsageMB: Number,
handleCount: Number
}
const mem = InDriver.systemProcessMemory();
InDriver.debug(
"INDriver Memory: " +
mem.privateUsageMB.toFixed(0) +
" MB");
const pg = InDriver.systemProcessMemory("postgres.exe");
InDriver.debug(
"PostgreSQL Memory: " +
pg.privateUsageMB.toFixed(0) +
" MB");
InDriver.systemProcessMemoryAll(processName: String = ""): Object
Returns aggregated memory statistics for all processes with the specified executable name.
If no process name is provided, statistics are aggregated for all running instances of the current InDriver executable.
optional, default: ""
Executable name to search for.
Examples:
{
ok: Boolean,
process: String,
processCount: Number,
workingSetMB: Number,
privateUsageMB: Number,
pagefileUsageMB: Number,
peakWorkingSetMB: Number,
peakPagefileUsageMB: Number,
quotaPagedPoolUsageMB: Number,
quotaNonPagedPoolUsageMB: Number,
handleCount: Number,
openFailedCount: Number,
accessDeniedCount: Number,
otherOpenErrorCount: Number,
memoryReadFailedCount: Number
}
const pg = InDriver.systemProcessMemoryAll("postgres.exe");
InDriver.debug(
"PostgreSQL Total Memory: " +
pg.privateUsageMB.toFixed(0) +
" MB (" +
pg.processCount +
" processes)");
InDriver.systemMemory(): Object
Returns memory statistics for the entire operating system.
Useful for monitoring physical memory usage, virtual memory usage, and memory pressure.
{
ok: Boolean,
memoryLoadPercent: Number,
totalPhysMB: Number,
availPhysMB: Number,
usedPhysMB: Number,
totalPageFileMB: Number,
availPageFileMB: Number,
usedPageFileMB: Number,
totalVirtualMB: Number,
availVirtualMB: Number,
usedVirtualMB: Number
}
const mem = InDriver.systemMemory();
InDriver.debug(
"RAM: " +
mem.usedPhysMB.toFixed(0) +
" / " +
mem.totalPhysMB.toFixed(0) +
" MB");
InDriver.systemSnapshot(): Object
Returns a combined snapshot of CPU usage, current process memory statistics, and system memory statistics.
Useful for periodic health monitoring and diagnostics.
{
cpuUsagePercent: Number,
processMemory: Object,
systemMemory: Object
}
Example
const s = InDriver.systemSnapshot();
InDriver.debug(
"CPU=" + s.cpuUsagePercent.toFixed(1) +
"% RAM=" + s.systemMemory.usedPhysMB.toFixed(0) +
"/" + s.systemMemory.totalPhysMB.toFixed(0) +
"MB");
Example
const s = InDriver.systemSnapshot();
InDriver.debug(JSON.stringify(s));
InDriver.uninstallHook(h: Number): void
Removes a previously installed hook interval.
InDriver.uninstallHook(60000);
InDriver.updateStatistics(stat: String): void
Updates runtime statistics/debug information for the task.
InDriver.updateStatistics(JSON.stringify({
rowsProcessed: 1000,
elapsedMs: 250
}));
HttpServerApi allows an InDriver task to start an embedded HTTP server, register endpoints, dispatch requests to JavaScript handler functions, and build HTTP responses from JavaScript return values. It is intended for lightweight REST endpoints, operator panels, local integration endpoints, and exposing task data over HTTP. The API is stateful: registered routes, debug mode, and CORS headers are stored inside the HttpServerApi instance.
HttpServerApi.debugMode(mode: Boolean = true): void
Enables or disables debug mode for the embedded HTTP server.
When debug mode is enabled:
When disabled:
The function only updates internal flag mDebugMode. It does not start or stop the server and does not affect already registered routes except changing runtime logging/error verbosity.
HttpServerApi.debugMode(true);
HttpServerApi.listen(httpServerCfg: String): Number
HttpServerApi.listen(httpServerCfg: Object = {}): Number
Starts the HTTP server and binds it to the configured address and port.
There are two overloads:
The object overload is internally converted to JSON text and then processed by the string overload.
If omitted:
If invalid:
If omitted:
Manual text previously suggested random port may be chosen when omitted, but current implementation sets default port to 8080. The code should be treated as source of truth here.
The server listens using QHttpServer::listen(address, port). After the bind attempt, a debug line is emitted showing the final address, returned port, and a textual classification such as:
let port = HttpServerApi.listen({
address: "Any",
port: 8080
});
let port = HttpServerApi.listen('{"address":"LocalHost","port":9000}');
HttpServerApi.route(path: String, method: String, functionName: String): Boolean
Registers an HTTP endpoint and binds it to a JavaScript function in global scope.
Incoming requests matching the normalized path and parsed HTTP method will invoke the specified JS function. The handler function is looked up dynamically by name in the global JS object at request time, not at registration time.
Behavior:
Examples:
"/orders"
"orders" // normalized to "/orders"
"" // normalized to "/"
Supported values:
If invalid:
Requirements:
If missing/blank:
If route exists physically already:
The JS function is called with one argument: req.
req contains fields documented in the manual:
When a request hits a registered route:
If handler returns object:
{
status: 200,
contentType: "application/json",
body: { ok: true }
}
then:
If handler returns:
HttpServerApi.route("/orders", "post", "createOrder");
function createOrder(req) {
return {
status: 200,
contentType: "application/json",
body: { ok: true }
};
}
HttpServerApi.route("status", "get", "getStatus");
function getStatus(req) {
return { message: "System OK" };
}
HttpServerApi.setCorsHeaders(headers: Object): void
Replaces the current set of custom CORS/response headers with a new JSON object.
Headers stored here are appended to every response generated by HttpServerApi, including normal responses and error responses. The function does not merge incrementally; it replaces the stored header object.
Typical examples:
HttpServerApi.setCorsHeaders({
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type"
});
onStartup:
InDriver.import("HttpServerApi");
HttpServerApi.debugMode(true);
HttpServerApi.setCorsHeaders({
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type"
});
HttpServerApi.route("/status", "get", "getStatus");
HttpServerApi.route("/orders", "post", "createOrder");
let port = HttpServerApi.listen({
address: "Any",
port: 8080
});
InDriver.debug("HTTP server started on port: " + port);
function getStatus(req) {
return {
status: 200,
contentType: "application/json",
body: { ok: true, path: req.path }
};
}
function createOrder(req) {
return { received: req.body };
}
CryptographicHashApi allows an InDriver task to calculate cryptographic hashes for text and raw binary data.
The API supports two usage modes:
The API is stateful: the selected algorithm and the internal hashing context are stored inside the instance.
Default algorithm: SHA256
The following algorithms are available:
Algorithm names are:
CryptographicHashApi.addData(text: String): Boolean
Adds text data to the current incremental hash.
The string is converted to UTF-8 before hashing.
type: String
Text to append to the current hash input.
Boolean
CryptographicHashApi.reset();
CryptographicHashApi.addData("Hello ");
CryptographicHashApi.addData("World");
CryptographicHashApi.addDataUtf8(data: QByteArray): Boolean
Adds raw byte data to the current incremental hash.
No encoding or conversion is performed.
type: QByteArray
Raw bytes.
Boolean
let bytes = InDriver.toUtf8("abc");
CryptographicHashApi.addDataUtf8(bytes);
CryptographicHashApi.algorithm(): String
Returns the currently selected algorithm.
String
Normalized algorithm name.
let algo = CryptographicHashApi.algorithm(); // "SHA256"
CryptographicHashApi.algorithms(): Array
Returns the list of all supported algorithms.
Array
Array of strings.
let list = CryptographicHashApi.algorithms();
CryptographicHashApi.hash(text: String): QByteArray
Computes a one-shot hash for a string.
The string is converted to UTF-8 and hashed immediately.
type: String
Input text.
QByteArray
Binary hash.
let hash = CryptographicHashApi.hash("abc");
CryptographicHashApi.hashHex(text: String): String
Computes a one-shot hash and returns it as hexadecimal string.
type: String
Input text.
String
Hex-encoded hash.
let hex = CryptographicHashApi.hashHex("abc");
CryptographicHashApi.hashHexUtf8(data: QByteArray): String
Computes a one-shot hash for raw bytes and returns hex string.
type: QByteArray
Input bytes.
String
Hex-encoded hash.
let bytes = InDriver.toUtf8("abc");
let hex = CryptographicHashApi.hashHexUtf8(bytes);
CryptographicHashApi.hashUtf8(data: QByteArray): QByteArray
Computes a one-shot hash for raw bytes.
type: QByteArray
Input bytes.
QByteArray
Binary hash.
let bytes = InDriver.toUtf8("abc");
let hash = CryptographicHashApi.hashUtf8(bytes);
CryptographicHashApi.isSupported(algorithm: String): Boolean
Checks whether a given algorithm is supported.
type: String
Algorithm name.
Boolean
if (CryptographicHashApi.isSupported("SHA3-256")) {
CryptographicHashApi.setAlgorithm("SHA3-256");
}
CryptographicHashApi.reset(): Boolean
Clears current incremental hash state.
Boolean
Always true.
CryptographicHashApi.reset();
CryptographicHashApi.result(): QByteArray
Returns binary hash for current incremental state.
QByteArray
Binary digest.
let digest = CryptographicHashApi.result();
CryptographicHashApi.resultHex(): String
Returns current hash as hexadecimal string.
String
Hex-encoded digest.
let hex = CryptographicHashApi.resultHex();
CryptographicHashApi.setAlgorithm(algorithm: String): Boolean
Sets the active hashing algorithm.
type: String
Algorithm name.
Boolean
CryptographicHashApi.setAlgorithm("SHA512");
InDriver.import("CryptographicHashApi");
let hex = CryptographicHashApi.hashHex("Hello World");
InDriver.import("CryptographicHashApi");
CryptographicHashApi.setAlgorithm("SHA256");
CryptographicHashApi.reset();
CryptographicHashApi.addData("part1");
CryptographicHashApi.addData("part2");
let hex = CryptographicHashApi.resultHex();
OPCUAClientApi provides OPC UA client access from JavaScript tasks. It supports:
The API stores client instances by clientName, so the same logical client can be reconfigured and reconnected by calling connect(...) again with the same name.
OPCUAClientApi.browse(
clientName: String,
nodeIdStr: String = "",
filter: String = "",
limit: Number = 100
): Array
Browses the OPC UA address space starting from a selected node and returns discovered nodes as a JSON array.
Supported special values in current implementation:
Behavior:
From source, the filter object supports at least:
Because the exact JS filter grammar is implemented deeper in helper classes and is not fully documented in the visible snippet, this argument should be documented as an implementation-defined filter string rather than over-specified.
InDriver.import("OPCUAClientApi");
OPCUAClientApi.connect(
"Client1",
'{"EndpointUrl":"opc.tcp://localhost:4840","Timeout":5000}'
);
let nodes = OPCUAClientApi.browse("Client1", "Objects", "", 50);
InDriver.debug(nodes);
OPCUAClientApi.connect(clientName: String, cfg: Object): Boolean
OPCUAClientApi.connect(clientName: String, cfg: String = ""): Boolean
Creates or reconfigures a named OPC UA client and attempts to connect it to a server.
The object overload is internally serialized to JSON and passed to the string overload.
Known documented field:
Example:
{
"EndpointUrl": "opc.tcp://localhost:4840",
"Timeout": 5000
}
InDriver.import("OPCUAClientApi");
let ok = OPCUAClientApi.connect(
"Client1",
'{"EndpointUrl":"opc.tcp://localhost:4840","Timeout":5000}'
);
if (!ok) {
InDriver.debug(OPCUAClientApi.lastError(), "critical");
}
OPCUAClientApi.disconnect(clientName: String): Boolean
Disconnects a previously created OPC UA client.
let ok = OPCUAClientApi.disconnect("Client1");
if (!ok) {
InDriver.debug(OPCUAClientApi.lastError(), "critical");
}
OPCUAClientApi.isConnected(clientName: String): Boolean
Checks whether the named OPC UA client is currently connected.
if (OPCUAClientApi.isConnected("Client1")) {
InDriver.debug("Client1 connected");
}
OPCUAClientApi.lastError(): String
Returns the last error message stored by the client API.
let err = OPCUAClientApi.lastError();
if (err) InDriver.debug(err, "critical");
OPCUAClientApi.readMultipleNodes(clientName: String, nodeCfg: String): Array
OPCUAClientApi.readMultipleNodes(clientName: String, nodeCfg: Array): Array
Reads values from multiple OPC UA nodes and returns results as JSON array.
Minimal documented example:
[
{ "nodeId": "ns=2;s=Temperature" }
]
The source follows the same pattern as other OPC methods: parse input JSON, perform operation, enrich same structure, return resulting QJsonArray.
let data = OPCUAClientApi.readMultipleNodes(
"Client1",
'[{"nodeId":"ns=2;s=Temperature"}]'
);
InDriver.debug(data);
OPCUAClientApi.runIterate(clients: String[] = [], timeout: Number = 0): void
Runs client iterate cycle for all clients or only selected clients.
Iterates through internal client map and calls runIterateClient(timeout) on:
OPCUAClientApi.runIterate();
OPCUAClientApi.runIterate(["Client1", "Client2"], 10);
This is a low-level helper. For many standard task flows, explicit connect/read/write is enough.
OPCUAClientApi.writeMultipleNodes(clientName: String, nodeCfg: String): Boolean
OPCUAClientApi.writeMultipleNodes(clientName: String, nodeCfg: Array): Boolean
Writes values to multiple OPC UA nodes.
Documented example:
[
{ "nodeId": "ns=2;s=Temperature", "value": 25 }
]
let ok = OPCUAClientApi.writeMultipleNodes(
"Client1",
'[{"nodeId":"ns=2;s=Temperature","value":25}]'
);
if (!ok) {
InDriver.debug(OPCUAClientApi.lastError(), "critical");
}
onStartup:
InDriver.import("OPCUAClientApi");
let ok = OPCUAClientApi.connect(
"Client1",
'{"EndpointUrl":"opc.tcp://localhost:4840","Timeout":5000}'
);
if (!ok) {
InDriver.debug(OPCUAClientApi.lastError(), "critical");
}
onHook:
let values = OPCUAClientApi.readMultipleNodes(
"Client1",
'[{"nodeId":"ns=2;s=Temperature"}]'
);
if (OPCUAClientApi.lastError()) {
InDriver.debug(OPCUAClientApi.lastError(), "critical");
return;
}
InDriver.debug(values);
OPCUAServerApi provides an embedded OPC UA server inside an InDriver task. It supports:
The server is stored as a single internal mOPCServer instance. Most operations fail if start(...) has not been called first.
OPCUAServerApi.addNodes(nodeCfg: String, folder: String = ""): Boolean
OPCUAServerApi.addNodes(nodeCfg: Array, folder: String = ""): Boolean
Adds nodes to the running OPC UA server.
Documented example:
[
{ "nodeId": "ns=2;s=TemperatureSensor", "name": "Sensor" }
]
Manual says default parent is "Objects", though the direct API default is empty string and underlying server logic determines how that is resolved.
InDriver.import("OPCUAServerApi");
OPCUAServerApi.start('{"EndpointUrl":"opc.tcp://localhost:4840"}');
let ok = OPCUAServerApi.addNodes( '[{"nodeId":"ns=2;s=TemperatureSensor","name":"Sensor"}]',
"Objects");
OPCUAServerApi.lastError(): String
Returns the last stored server API error.
let err = OPCUAServerApi.lastError();
if (err) InDriver.debug(err, "critical");
OPCUAServerApi.readMultipleNodes(nodeCfg: String): Array
OPCUAServerApi.readMultipleNodes(nodeCfg: Array): Array
Reads values from multiple nodes exposed by the embedded OPC UA server.
Documented example:
[
{ "nodeId": "ns=2;s=Temperature" }
]
let data = OPCUAServerApi.readMultipleNodes(
'[{"nodeId":"ns=2;s=Temperature"}]'
);
InDriver.debug(data);
OPCUAServerApi.start(cfg: String = ""): void
Starts the embedded OPC UA server using JSON configuration.
Documented example:
{
"EndpointUrl": "opc.tcp://localhost:4840"
}
Because start() returns void, success must be inferred by:
InDriver.import("OPCUAServerApi");
OPCUAServerApi.start('{"EndpointUrl":"opc.tcp://localhost:4840"}');
OPCUAServerApi.stop(): void
Stops the embedded OPC UA server.
onShutdown:
OPCUAServerApi.stop();
OPCUAServerApi.writeMultipleNodes(nodeCfg: String): Boolean
OPCUAServerApi.writeMultipleNodes(nodeCfg: Array): Boolean
Writes values to multiple nodes hosted by the embedded OPC UA server.
Documented example:
[
{ "nodeId": "ns=2;s=Temperature", "value": 22.5 }
]
let ok = OPCUAServerApi.writeMultipleNodes( '[{"nodeId":"ns=2;s=Temperature","value":22.5}]' );
if (!ok) {
InDriver.debug(OPCUAServerApi.lastError(), "critical");
}
onStartup:
InDriver.import("OPCUAServerApi");
OPCUAServerApi.start('{"EndpointUrl":"opc.tcp://localhost:4840"}');
let ok = OPCUAServerApi.addNodes(
'[{"nodeId":"ns=2;s=TemperatureSensor","name":"Sensor"}]',
"Objects"
);
if (!ok) {
InDriver.debug(OPCUAServerApi.lastError(), "critical");
}
}
onHook:
OPCUAServerApi.writeMultipleNodes(
'[{"nodeId":"ns=2;s=Temperature","value":22.5}]'
);
onShutdown:
OPCUAServerApi.stop();
RestApi provides HTTP client functionality for JavaScript tasks. It supports:
The API is stateful. It stores:
RestApi.begin(): void
Starts a transaction block for collecting multiple REST requests before executing them together.
After calling begin():
RestApi.begin();
RestApi.sendRequest("Krakow");
RestApi.sendRequest("Chicago");
RestApi.commitWait();
RestApi.commit(): void
Executes all queued requests collected after begin(), but does not wait for their completion.
After commit(), you usually need to call:
RestApi.wait();
or
RestApi.wait(timeout);
RestApi.begin();
RestApi.sendRequest("Krakow");
RestApi.sendRequest("Chicago");
RestApi.commit();
RestApi.wait();
RestApi.commitWait(): void
Executes all queued requests and waits until all are finished or timeout occurs.
Internally equivalent to:
RestApi.commit();
RestApi.wait();
RestApi.begin();
RestApi.sendRequest("Krakow");
RestApi.sendRequest("Chicago");
RestApi.commitWait();
RestApi.defineRequest(reqName: String, cfg: String): void
RestApi.defineRequest(reqName: String, cfg: Object): void
Defines a named REST request configuration that can later be executed using:
RestApi.sendRequest(reqName)
The request definition supports:
type: String
Logical request name used later by:
RestApi.sendRequest(reqName)
RestApi.getData(reqName)
RestApi.getRawHeader(reqName, headerName)
RestApi.getRawHeaderPairs(reqName)
RestApi.isSucceeded(reqName)
Examples:
"Weather"
"PIWebApi"
"OrderSync"
"Mass1"
type: String | Object
JSON configuration describing the request.
Recommended form:
RestApi.defineRequest("Test", {
...
});
type: String
Target request URL.
Example:
url: "https://api.example.com/data"
Example (PI Web API):
url: "https://ZGHTIMS1.TIMS.local/piwebapi/streams/.../value"
Required.
type: String
HTTP method.
Supported values:
"get"
"post"
"put"
Use lowercase.
If unsupported:
Example:
type: "get"
type: Number
Request timeout in milliseconds.
Default:
3000
Example:
timeout: 30000
type: Object
Optional.
Custom HTTP headers.
Example:
headers: {
Accept: "application/json",
"User-Agent": "InDriver"
}
Supported Qt known headers:
ContentDispositionHeader
ContentTypeHeader
ContentLengthHeader
CookieHeader
SetCookieHeader
ETagHeader
IfMatchHeader
IfNoneMatchHeader
LastModifiedHeader
LocationHeader
ServerHeader
UserAgentHeader
Any other key is sent as raw HTTP header.
Example:
headers: {
Authorization: "Bearer xxxxx",
Accept: "application/json"
}
type: String | Object | Array
Optional.
Payload for:
post
put
Behavior:
If string: sent unchanged
If object/array: serialized to JSON
Example:
rawData: {
id: 123,
status: "new"
}
type: Object
Optional.
Challenge-based authentication using QAuthenticator.
Supported for:
Example:
auth: {
mode: "authenticator",
user: "inanalitics",
password: "inanalitics"
}
type: String
Supported:
"authenticator"
Behavior:
If server returns: 401 Unauthorized WWW-Authenticate
RestApi responds using: Authenticator with configured credentials.
If auth exists but credentials missing:
authentication challenge is logged but authentication fails.
Alternative authentication method.
Use explicit header:
headers: {
Authorization: "Basic xxxxxxxxx"
}
Example:
headers: {
Authorization: "Basic sjlksdjflkdjklsjfkl="
}
Useful for:
type: Object
Optional.
SSL/TLS behavior.
type: Boolean
If true:
SSL certificate validation errors are ignored.
Example:
ssl: {
ignoreErrors: true
}
Useful for:
type: Boolean
Default:
true
If false:
certificate validation disabled.
Example:
ssl: {
verifyPeer: false
}
type: String
Supported:
"tls1.2"
"tls1.3"
"tls1.2+"
"tlsv1.2"
"tlsv1.3"
"tlsv1.2orlater"
Example:
ssl: {
protocol: "tls1.2"
}
type: Boolean
Optional.
If true: HTTP/2 disabled.
Useful for compatibility with:
Example:
disableHttp2: true
type: Boolean
Optional.
If true: request uses a dedicated NetworkAccessManager.
Example:
dedicatedManager: true
Useful for problematic servers with:
Tradeoff:
slightly higher overhead due to manager creation/destruction.
type: Boolean
Default:
true
If true:
HTTP errors mark request as failed.
Examples:
401
403
404
500
cause:
RestApi.isSucceeded(reqName) == false
If false: only timeout / transport failure matters.
Example:
failOnHttpError: false
Useful for debugging APIs returning useful error payloads.
type: Boolean
Enables detailed logging.
Example:
debugMode: true
type: String
Supported:
"basic"
"headers"
"full"
Logs:
Adds:
Adds:
Returns:
true
only if:
HTTP success:
200–299
If:
failOnHttpError: false
then HTTP error responses may still return success.
RestApi.defineRequest("Weather", {
url: "https://danepubliczne.imgw.pl/api/data/synop",
type: "get",
timeout: 10000,
headers: {
Accept: "application/json"
}
});
RestApi.defineRequest("Mass1", {
url: "https://server/piwebapi/streams/.../value",
type: "get",
timeout: 30000,
dedicatedManager: true,
failOnHttpError: true,
auth: {
mode: "authenticator",
user: "user",
password: "pass"
},
ssl: {
ignoreErrors: true,
verifyPeer: false,
protocol: "tls1.2"
},
disableHttp2: true,
headers: {
Accept: "application/json",
"User-Agent": "InDriver"
}
});
RestApi.defineRequest("CreateOrder", {
url: "https://api.example.com/orders",
type: "post",
headers: {
ContentTypeHeader: "application/json"
},
rawData: {
id: 123,
status: "new"
}
});
Use:
type: "get"
type: "post"
type: "put"
Prefer:
RestApi.defineRequest("name", { ... })
Use:
auth
for challenge authentication.
Use:
Authorization
header for manual auth.
Use:
dedicatedManager: true
for problematic enterprise authentication servers.
Use:
debugMode: true
when troubleshooting integrations.
Use:
failOnHttpError: false
only for diagnostics.
RestApi.getData(reqName: String): String
Returns the response body stored for the named request from the most recent completed REST execution.
const data = RestApi.getData("Krakow");
InDriver.debug(data);
let obj = JSON.parse(RestApi.getData("Krakow"));
RestApi.getRawHeader(reqName: String, headerName: String): String
Returns a single response header value for the named request.
let contentType = RestApi.getRawHeader("Krakow", "Content-Type");
RestApi.getRawHeaderPairs(reqName: String): String
Returns all raw response headers for the named request as serialized JSON text.
let headersJson = RestApi.getRawHeaderPairs("Krakow");
InDriver.debug(headersJson);
RestApi.isSucceeded(): Boolean
Returns the success status of the last REST execution block.
The value is backed by internal mTransactionSucceeded. It becomes true when wait(...) finishes before timeout, not necessarily only when every HTTP status code is “successful”. Network reply errors are logged as fatal, but isSucceeded() is not set directly from HTTP status codes.
isSucceeded() mainly indicates:
if (RestApi.isSucceeded()) {
let data = RestApi.getData("Krakow");
InDriver.debug(data);
}
RestApi.sendRequest(reqName: String): Boolean
RestApi.sendRequest(reqName: String, jsonPayload: Object): Boolean
RestApi.sendRequest(reqName: String, data: String): Boolean
Sends a REST request using either:
Uses previously defined request configuration.
Serializes the object to JSON text and delegates to string overload.
Uses string payload/config override.
If begin() was not called:
If begin() was called:
For POST/PUT:
RestApi.defineRequest("Krakow", {
url: "https://api.openweathermap.org/data/2.5/weather?appid=your_app_id&q=krakow",
timeout: 5000,
type: "get",
headers: {
ContentTypeHeader: "application/json"
}
});
RestApi.sendRequest("Krakow");
RestApi.wait();
if (RestApi.isSucceeded()) {
const data = RestApi.getData("Krakow");
InDriver.debug(data);
}
RestApi.begin();
RestApi.sendRequest("Krakow");
RestApi.sendRequest("Chicago");
RestApi.commitWait();
RestApi.defineRequest("CreateOrder", {
url: "https://example.com/api/orders",
timeout: 5000,
type: "post",
headers: {
ContentTypeHeader: "application/json"
}
});
RestApi.sendRequest("CreateOrder", {
id: 123,
status: "new"
});
RestApi.wait(): void
RestApi.wait(timeout: Number): void
Waits until all replies from the last commit() finish or until timeout occurs.
If omitted:
If timeout occurs:
RestApi.commit();
RestApi.wait();
RestApi.commit();
RestApi.wait(10000);
This function does not return false on timeout.
Instead, it raises a JS error in the runtime.
From current source:
If timeout <= 0 or missing:
onsslErrors(...) deletes the reply object and ignores detailed SSL errors in the shown implementation.
On each finished reply, runtime stores:
onStartup:
InDriver.import("RestApi");
RestApi.defineRequest("Krakow", {
url: "https://api.openweathermap.org/data/2.5/weather?appid=your_app_id&q=krakow",
timeout: 5000,
type: "get",
headers: {
ContentTypeHeader: "application/json"
}
});
InDriver.installHook(60000);
}
onHook:
RestApi.sendRequest("Krakow");
RestApi.wait();
if (RestApi.isSucceeded()) {
const data = RestApi.getData("Krakow");
InDriver.debug(data);
}
RestApi.begin();
RestApi.sendRequest("Krakow");
RestApi.sendRequest("Chicago");
RestApi.commitWait();
if (RestApi.isSucceeded()) {
const krakow = RestApi.getData("Krakow");
const chicago = RestApi.getData("Chicago");
}
RestApi.defineRequest("CreateOrder", {
url: "https://example.com/api/orders",
timeout: 5000,
type: "post",
headers: {
ContentTypeHeader: "application/json"
}
});
RestApi.sendRequest("CreateOrder", {
id: 123,
status: "new"
});
RestApi.wait();
When generating code using RestApi, follow these rules:
ModbusApi provides Modbus device communication for JavaScript tasks. It supports:
The API is stateful. It stores:
ModbusApi.begin(): void
Starts a Modbus transaction block.
After begin():
ModbusApi.begin();
ModbusApi.readDevice("Moxa1", '{"name":"coils","type":"COILS","address":1,"size":8}');
ModbusApi.commitWait();
ModbusApi.commit(): void
Executes all queued Modbus requests collected after begin(), but does not wait for completion.
After commit(), you normally call:
ModbusApi.wait();
or
ModbusApi.wait(timeout);
ModbusApi.begin();
ModbusApi.readDevice("Moxa1", '{"name":"coils","type":"COILS","address":1,"size":8}');
ModbusApi.commit();
ModbusApi.wait();
ModbusApi.commitWait(): void
Executes all queued requests and waits for their completion or timeout.
Internally equivalent to:
ModbusApi.commit();
ModbusApi.wait();
ModbusApi.begin();
ModbusApi.readDevice("Moxa1", '{"name":"coils","type":"COILS","address":1,"size":8}');
ModbusApi.commitWait();
ModbusApi.connectDevice(deviceName: String, cfg: String): void
Creates a named Modbus device connection definition.
If the device name does not already exist, a new JSModbusDevice instance is created and stored.
If the device name already exists, the current code does nothing — it does not overwrite or reconfigure the existing device.
Supported keys from manual:
Minimal TCP example:
{
"mode": "TCP",
"networkAddress": "192.168.0.22"
}
Full TCP example:
{
"mode": "TCP",
"networkAddress": "192.168.0.22",
"networkPort": 502,
"timeoutMs": 3000,
"numberOfRetries": 3
}
Supported keys from manual:
Minimal RTU example:
{
"mode": "RTU",
"serialPortName": "COM1"
}
Full RTU example:
{
"mode": "RTU",
"serialPortName": "COM1",
"baudRate": 9600,
"parity": 2,
"dataBits": 8,
"stopBits": 1,
"timeoutMs": 3000,
"numberOfRetries": 3
}
ModbusApi.connectDevice(
"IOLogic",
'{"mode":"TCP","networkAddress":"192.168.0.22"}'
);
ModbusApi.getAllData(): String
Returns the data collected in the last Modbus transaction for all devices and all requests, serialized as JSON string.
Iterates over all requests in the last transaction and merges their result data into one JSON object.
{
"Moxa1": {
"Read": {
"coilsA": {
"1": true,
"2": false
}
}
}
}
let dataJson = ModbusApi.getAllData();
let data = JSON.parse(dataJson);
ModbusApi.getDeviceData(deviceName: String): String
Returns the last transaction data for one selected device, serialized as JSON string.
Filters last transaction requests by deviceName and builds JSON only for that device.
let deviceData = JSON.parse(ModbusApi.getDeviceData("Moxa1"));
ModbusApi.getDeviceRequestData(deviceName: String, registerName: String): String
Returns the last transaction data for one selected device and one selected request/register name, serialized as JSON string.
Filters last transaction requests by both device name and request/register name.
let coilsA = JSON.parse(ModbusApi.getDeviceRequestData("Moxa1", "coilsA"));
ModbusApi.getDeviceRequestValue(
deviceName: String,
registerName: String,
addresses: Number[]
): Number
Returns a numeric value composed from selected addresses of the result for the specified device and request.
let value = ModbusApi.getDeviceRequestValue("Moxa1", "coilsA", [1, 2, 3]);
ModbusApi.isLastTransactionCompleted(deviceName: String): Boolean
ModbusApi.isLastTransactionCompleted(): Boolean
Checks whether the last Modbus transaction completed without request-level error/timeout.
if (!ModbusApi.isLastTransactionCompleted("Moxa1")) {
InDriver.debug("Moxa1 transaction incomplete", "critical");
}
This function checks request completion/error state, while isSucceeded() reflects whether the whole wait cycle completed before timeout. Both can be useful and are not exactly the same thing.
ModbusApi.isSucceeded(): Boolean
Returns whether the last Modbus transaction completed before the wait timeout.
mTransactionSucceeded becomes true only when wait(...) finishes because all requests completed before the timer expired. If timeout occurs, it remains false.
if (ModbusApi.isSucceeded()) {
let data = ModbusApi.getAllData();
}
ModbusApi.readDevice(deviceName: String, registerCfg: String): void
Queues or executes a Modbus read request for the specified device.
Supported keys from manual:
Example:
{
"name": "coils1",
"deviceAddress": 1,
"type": "COILS",
"address": 1,
"size": 8
}
ModbusApi.readDevice(
"IOLogic",
'{"name":"coils1","type":"COILS","address":1,"size":8}'
);
ModbusApi.wait(): void
ModbusApi.wait(maxTimeOut: Number): void
Waits for completion of the last committed Modbus transaction.
If omitted:
ModbusApi.commit();
ModbusApi.wait();
ModbusApi.commit();
ModbusApi.wait(10000);
ModbusApi.writeDevice(deviceName: String, registerCfg: String): void
Queues or executes a Modbus write request for the specified device.
Supported keys from manual:
Example:
{
"name": "coils1",
"deviceAddress": 1,
"type": "COILS",
"address": 1,
"data": [1, 1, 1]
}
begin()
append request
commitWait()
ModbusApi.writeDevice(
"Moxa",
'{"name":"coils1","type":"COILS","address":1,"data":[1,1,1]}'
);
onHook()
ModbusApi.readDevice(
"IOLogic",
'{"name":"coils1","type":"COILS","address":1,"size":8}'
);
if (ModbusApi.isSucceeded()) {
let data = JSON.parse(ModbusApi.getAllData());
InDriver.debug(data);
}
ModbusApi.begin();
ModbusApi.readDevice("Moxa1", '{"name":"coilsA","type":"COILS","address":1,"size":4}');
ModbusApi.readDevice("Moxa1", '{"name":"coilsB","type":"COILS","address":5,"size":4}');
ModbusApi.readDevice("Moxa2", '{"name":"coils","type":"COILS","address":1,"size":8}');
ModbusApi.commitWait();
if (ModbusApi.isSucceeded()) {
let all = JSON.parse(ModbusApi.getAllData());
}
ModbusApi.begin();
RestApi.begin();
RestApi.sendRequest("Krakow");
ModbusApi.readDevice("IOLogic", '{"name":"coils1","type":"COILS","address":1,"size":8}');
ModbusApi.commit();
RestApi.commit();
ModbusApi.wait();
RestApi.wait();
const weatherData = RestApi.getData("Krakow");
const modbusData = ModbusApi.getAllData();
When generating code with ModbusApi, follow these rules:
DeviceAPI is a generic transaction layer for reading data from external devices connected through:
It provides:
DeviceAPI is intended to be used through derived classes such as:
deviceApi.begin(): void
Starts a grouped device transaction.
modbus.begin();
mbus.begin();
deviceApi.commit(): void
Starts execution of queued device requests without blocking until completion.
modbus.begin();
modbus.execute(req1);
modbus.execute(req2);
modbus.commit();
deviceApi.commitWait(timeout: Number = 0): void
Executes queued device requests and waits until they finish or the timeout is reached.
ModbusApi.begin();
MacREJ5R_readVQTAsyncReq("BB_Pion2_Gaz", 1);
ModbusApi.commitWait();
deviceApi.debugModeEnabled(enabled: Boolean): void
Enables or disables verbose device debug logging.
When enabled, the API logs:
modbus.debugModeEnabled(false);
mbus.debugModeEnabled(false);
deviceApi.execute(deviceConfig: Object): void
Queues a device request for execution.
Every request should provide:
Depending on protocol, it also needs:
modbus.execute(ar252_ValuesModbusRequest("ar252_1", 1, "socket_Pion2_Com1", "RTUoverTCP") );
deviceApi.getDeviceData(device: String = ""): Object
Returns collected device data.
Object
let modbusJSON = modbus.getDeviceData();
let mbusJSON = mbus.getDeviceData();
deviceApi.getStatistics(): Object
Returns execution statistics grouped by communication port and device name.
Typical statistics include:
let stats = modbus.getStatistics();
InDriver.debug(stats);
deviceApi.waitForDevices(otherDevices: Array = [], timeout: Number = 0): void
Waits until the current API instance and optional additional API instances finish execution.
modbus.commit();
mbus.commit();
modbus.waitForDevices([mbus]);
This matches your usage pattern.
DeviceAPI selects transport based on deviceConfig.mode:
Requests are sent through:
On timeout:
MbusAPI is a device-level API for M-Bus meters. It extends DeviceAPI and adds:
Supported device types in the attached scripts:
mbusApi.exec(deviceConfig: Object): Boolean
Creates the correct M-Bus device handler and starts the request sequence.
Common M-Bus-specific fields:
Boolean
mbus.execute({
name: "BB_LakPion2_Woda",
device: "KamstrupMultical",
fnc: "readData",
item: "values",
deviceId: 0,
address: 1,
frameSendDelay: 100,
commPort: "socket_Pion2_Com2",
mode: "TCP"
});
This matches your current usage.
mbusApi.receive(data: Object): void
Processes received transport data and forwards it to the correct device instance.
The attached M-Bus device classes follow a common flow:
ModbusAPI is a device-level API for Modbus communication. It extends DeviceAPI and supports:
modbusApi.exec(deviceConfig: Object): Boolean
Creates the correct Modbus request handler and starts the request.
Additional fields depend on function:
Boolean
modbus.execute({
name: "BB_LakierniaPion2_Cieplo",
item: "values",
fnc: "ReadHoldingRegisters",
slaveAddress: 94,
startAddress: 0,
quantity: 54,
frameSendDelay: 20,
commPort: "socket_Pion2_Com1",
mode: "RTUoverTCP"
});
This matches the helper-generated requests used in your script.
modbusApi.receive(data: Object): void
Processes received Modbus data and forwards it to the correct request instance.
For mode: "TCP", request frames are converted from RTU format into Modbus TCP format before sending. For RTU responses, CRC validation is performed; for TCP responses, the MBAP header is stripped before Modbus response parsing.
The attached device libraries build reusable device-specific logic on top of ModbusAPI or MbusAPI. They follow a consistent pattern:
This is the recommended level for AI-generated code, because protocol details stay separate from business logic.
The apar.js helper provides support for the AR252 device. It includes:
The parser returns:
modbus.execute(
ar252_ValuesModbusRequest("ar252_1", 1, "socket_Pion2_Com1", "RTUoverTCP")
);
let modbusJSON = modbus.getDeviceData();
if ("ar252_1" in modbusJSON) {
let d = ar252_ParseValuesModbus(modbusJSON.ar252_1.values);
}
The common.js helper contains support for CMK03. It provides request builders such as:
and parsers such as:
Typical parsed values:
The kamstrup.js helper includes:
The parser returns a normalized object containing fields such as:
modbus.execute(
multical_KeyValuesModbusRequest(
"BB_LakierniaPion2_Cieplo",
94,
"socket_Pion2_Com1",
"RTUoverTCP"
)
);
This matches your script.
The plum.js helper provides support for MacREJ5R. It includes:
Typical parsed values:
ModbusApi.begin();
MacREJ5R_readVQTAsyncReq("BB_Pion2_Gaz", 1);
ModbusApi.commitWait();
let data = JSON.stringify(MacREJ5R_readVQTAsyncProcess("BB_Pion2_Gaz"));
This matches your current pattern.
The mbusAPI.js file contains three device classes:
mbus.execute({
name: "BB_LakPion2_Woda",
device: "KamstrupMultical",
fnc: "readData",
item: "values",
deviceId: 0,
address: 1,
frameSendDelay: 100,
commPort: "socket_Pion2_Com2",
mode: "TCP"
});
onStartup:
InDriver.import("SerialPortApi");
InDriver.import("TcpSocketApi");
InDriver.import("ModbusApi");
InDriver.loadScript("deviceLibrary/deviceAPI.js");
InDriver.loadScript("deviceLibrary/modbusAPI.js");
InDriver.loadScript("deviceLibrary/mbusAPI.js");
InDriver.loadScript("deviceLibrary/kamstrup.js");
InDriver.loadScript("deviceLibrary/plum.js");
InDriver.loadScript("deviceLibrary/apar.js");
TcpSocketApi.connect("socket_Pion2_Com1", {
address: "10.4.107.186",
port: 4001,
timeout: 5000,
mode: "Buffer"
});
TcpSocketApi.connect("socket_Pion2_Com2", {
address: "10.4.107.186",
port: 4002,
timeout: 5000,
mode: "Buffer"
});
var modbus = new ModbusAPI();
var mbus = new MbusAPI();
modbus.debugModeEnabled(false);
mbus.debugModeEnabled(false);
onHook:
modbus.begin();
mbus.begin();
modbus.execute(
multical_KeyValuesModbusRequest(
"BB_LakierniaPion2_Cieplo",
94,
"socket_Pion2_Com1",
"RTUoverTCP"
)
);
modbus.execute(
ar252_ValuesModbusRequest("ar252_1", 1, "socket_Pion2_Com1", "RTUoverTCP")
);
mbus.execute({
name: "BB_LakPion2_Woda",
device: "KamstrupMultical",
fnc: "readData",
item: "values",
deviceId: 0,
address: 1,
frameSendDelay: 100,
commPort: "socket_Pion2_Com2",
mode: "TCP"
});
modbus.commit();
mbus.commit();
modbus.waitForDevices([mbus], 5000);
let modbusJSON = modbus.getDeviceData();
let mbusJSON = mbus.getDeviceData();
if ("ar252_1" in modbusJSON) {
let d = ar252_ParseValuesModbus(modbusJSON.ar252_1.values);
InDriver.sqlExecute(
"LocalPGSQL",
`select tsapiinsert('public', 'measurements', 'BB_ar252_1', '${ts.toISOString()}', '${JSON.stringify(d)}')`
);
}
When generating code with DeviceAPI, MbusAPI, ModbusAPI, and the device helper libraries, follow these rules:
InDriver.import("SerialPortApi");
InDriver.import("TcpSocketApi");
InDriver.import("ModbusApi");
InDriver.loadScript("deviceLibrary/deviceAPI.js");
InDriver.loadScript("deviceLibrary/modbusAPI.js");
InDriver.loadScript("deviceLibrary/mbusAPI.js");
begin → execute → commit/commitWait → getDeviceData
modbus.commit();
mbus.commit();
modbus.waitForDevices([mbus]);
Use:
SmtpApi provides SMTP email functionality for JavaScript tasks in InDriver.
It supports:
The API is stateful, similar to RestApi and ModbusApi.
SmtpApi.send(...)
SmtpApi.begin();
SmtpApi.send(...);
SmtpApi.send(...);
SmtpApi.commit();
SmtpApi.begin(): void
Starts a new email transaction.
SmtpApi.begin();
SmtpApi.commit(): Boolean
Sends all emails queued since the last begin() call.
Boolean
SmtpApi.begin();
SmtpApi.send({...});
SmtpApi.send({...});
if (!SmtpApi.commit()) {
InDriver.debug(SmtpApi.lastError(), "critical");
}
SmtpApi.configure(cfg: String): Boolean
SmtpApi.configure(cfg: Object): Boolean
Configures SMTP server settings for subsequent send() operations.
cfg
Supported fields
Field | Type | Required | Description |
host | String | ✅ | SMTP server hostname |
port | Number | ❌ | SMTP port |
username | String | ❌ | authentication username |
password | String | ❌ | authentication password |
ssl | Boolean | ❌ | enable SSL/TLS |
Boolean
SmtpApi.configure({
host: "smtp.company.com",
port: 587,
username: "user",
password: "pass",
ssl: true
});
SmtpApi.send(mail: String): Boolean
SmtpApi.send(mail: Object): Boolean
Adds an email to the queue or sends it immediately depending on execution mode.
Supported fields
Field | Type | Required | Description |
from | String | ✅ | sender email |
to | String / Array | ✅ | recipient(s) |
subject | String | ❌ | email subject |
body | String | ❌ | email body |
attachments | Array | ❌ | list of file paths |
Boolean
SmtpApi.send({
from: "system@company.com",
to: "admin@company.com",
subject: "Alert",
body: "Machine stopped"
});
SmtpApi.begin();
SmtpApi.send({
from: "system@company.com",
to: "a@company.com",
subject: "Report A"
});
SmtpApi.send({
from: "system@company.com",
to: "b@company.com",
subject: "Report B"
});
SmtpApi.commit();
SmtpApi.lastError(): String
Returns the last SMTP error message.
String
if (!SmtpApi.commit()) {
InDriver.debug(SmtpApi.lastError(), "critical");
}
SmtpApi.configure({
host: "smtp.company.com",
from: "indriver@company.com"
});
SmtpApi.send({
to: "maintenance@company.com",
subject: "ALERT",
body: "Machine stopped"
});
SmtpApi.configure({
host: "smtp.company.com",
from: "indriver@company.com"
});
SmtpApi.begin();
for (let user of users) {
SmtpApi.send({
to: user.email,
subject: "Daily report",
body: user.report
});
}
if (!SmtpApi.commit()) {
InDriver.debug(SmtpApi.lastError(), "critical");
}
When generating code using SmtpApi, follow these rules:
SmtpApi.configure(...)
begin → send → commit
if (!SmtpApi.commit()) → lastError()
TsApi provides time-series aggregation support for InDriver tasks. It is built around an internal aggregator engine defined in C++, while the SQL package (tsapi.sql) defines the standard database structures and helper SQL functions used by the engine. In practice, TsApi is responsible for creating and maintaining aggregation tables derived from a raw JSON time-series table.
The SQL package installs a set of helper functions and standard table structures. The most important pieces are:
Created by tsapicreatetable(schema_, name_):
This is the base table for raw data collection. Each row represents one source at one timestamp with JSON payload in data.
Created by tsapicreateaggregationtable(schema_, name_):
(source text, ts timestamp with time zone, data jsonb, extras jsonb)
This is the standard structure used by the aggregation engine. The extras column stores aggregation metadata/statistics. In the SQL package, helper functions such as tsapiselectaggdata expose extras->>'status', which confirms that extras is part of the standard aggregation contract.
Aggregation Table column extras [JSON] | Aggregation Table column data [JSON] |
The package also installs functions such as:
tsapiinstall(schema_ text) returns void
Installs the TSAPI package in the selected schema. This function registers the API in inapi_installedapis and creates the TSAPI SQL functions such as table creation, inserts, and selects.
select tsapiinstall('public');
tsapiuninstall(schema_ text) returns void
Uninstalls TSAPI from the selected schema. It removes the TSAPI entry from inapi_installedapis and drops the installed TSAPI functions.
select tsapiuninstall('public');
tsapiversion() returns table (ver text, dbtype text)
Returns the installed TSAPI version and the target database type. In the attached script, the reported values are ver = '2.0' and dbtype = 'QPSQL'.
select * from tsapiversion();
tsapicreatetable(schema_ text, name_ text) returns void
Creates a standard raw time-series table if it does not already exist. The created table contains columns source, ts, and data, and TSAPI also creates indexes for ts and source.
select tsapicreatetable('public', 'measurements');
tsapicreateaggregationtable(schema_ text, name_ text) returns void
Creates a standard aggregation table if it does not already exist. The created table contains source, ts, data, and extras, and TSAPI also creates indexes for ts and source.
select tsapicreateaggregationtable('public', 'measurements_1h');
tsapideletetable(schema_ text, name_ text) returns void
Drops a table if it exists, using CASCADE.
select tsapideletetable('public', 'measurements_old');
tsapiinsert(
schema_ text,
table_ text,
source_ text,
ts_ timestamp with time zone,
data_ jsonb
) returns void
Inserts a single raw time-series record into a TSAPI table. The inserted row contains the specified source, timestamp, and JSON payload in data.
select tsapiinsert(
'public',
'measurements',
'BB_ar252_1',
now(),
'{"t": 21.4, "rH": 45.0}'::jsonb
);
tsapiinsertvalues(
schema_ text,
table_ text,
source_ text,
data_ json
) returns void
Bulk inserts raw time-series values for one source from a JSON array. Before inserting, the function deletes existing rows for the same source starting from the minimum incoming timestamp used in the JSON array logic. The input array is expected to contain objects with ts and data.
[
{
"ts": "2026-04-21T10:00:00Z",
"data": { "t": 21.4 }
}
]
This structure is implied by the function reading i->>'ts' and i->>'data'.
select tsapiinsertvalues(
'public',
'measurements',
'BB_ar252_1',
'[
{"ts":"2026-04-21T10:00:00Z","data":{"t":21.4}},
{"ts":"2026-04-21T10:01:00Z","data":{"t":21.5}}
]'::json
);
tsapiinsertagg(
schema_ text,
table_ text,
source_ text,
dataandextras_ json
) returns void
Bulk inserts aggregated time-series rows into an aggregation table. Before inserting, it finds the minimum timestamp in the provided JSON array and deletes existing rows for that source from that timestamp onward. Then it inserts rows containing ts, data, and extras.
[
{
"ts": "2026-04-21T10:00:00Z",
"data": { "avg": 21.4 },
"extras": { "status": "ok" }
}
]
This structure is required because the function reads ts, data, and extras from each JSON element.
select tsapiinsertagg(
'public',
'measurements_1h',
'BB_ar252_1',
'[
{
"ts":"2026-04-21T10:00:00Z",
"data":{"avg":21.4},
"extras":{"status":"ok"}
}
]'::json
);
tsapiselectdistinctsources(schema_ text, table_ text)
Returns the distinct source names present in the selected table.
select * from tsapiselectdistinctsources('public', 'measurements');
tsapiselect(
schema_ text,
table_ text,
source_ text,
tsfrom_ text,
tsto_ text,
limit_ integer
) returns table (ts timestamp with time zone, data jsonb)
Returns raw time-series rows for one source, optionally filtered by start time, end time, and row limit. Results are ordered ascending by ts when a positive limit is used.
select * from tsapiselect(
'public',
'measurements',
'BB_ar252_1',
'2026-04-21T00:00:00Z',
'2026-04-21T23:59:59Z',
1000
);
tsapiselectfirst(schema_ text, table_ text, source_ text)
Returns the earliest raw row for the selected source.
select * from tsapiselectfirst('public', 'measurements', 'BB_ar252_1');
tsapiselectlast(schema_ text, table_ text, source_ text)
Returns the latest raw row for the selected source.
select * from tsapiselectlast('public', 'measurements', 'BB_ar252_1');
tsapiselectlastn(
schema_ text,
table_ text,
source_ text,
n_ integer
)
Returns the last n raw time-series samples for the selected source.
This function is intended for cases where the caller needs the most recent group of samples instead of only one latest row. Typical use cases include:
select * from tsapiselectlastn(
'public',
'measurements',
'BB_ar252_1',
100
);
select * from tsapiselectlastn(
'public',
'measurements',
'BB_Pion2_Gaz',
10
);
This returns the 10 most recent samples for BB_Pion2_Gaz.
Use tsapiselectlastn(...) for:
tsapiselectprevious(
schema_ text,
table_ text,
source_ text,
ts_ text
)
Returns the latest raw row strictly earlier than the provided timestamp.
select * from tsapiselectprevious(
'public',
'measurements',
'BB_ar252_1',
'2026-04-21T10:00:00Z'
);
tsapiselectagg(
schema_ text,
table_ text,
source_ text,
tsfrom_ text,
tsto_ text,
limit_ integer
)
Returns aggregation rows for one source, including both aggregated data and extras. It supports optional time bounds and an optional limit.
Same semantics as tsapiselect(...), but for aggregation tables.
select * from tsapiselectagg(
'public',
'measurements_1h',
'BB_ar252_1',
'',
'',
100
);
tsapiselectaggdata(
schema_ text,
table_ text,
source_ text,
tsfrom_ text,
tsto_ text,
limit_ integer
)
Returns aggregation rows for one source, but instead of the full extras JSON it returns only extras->>'status' as status.
Same semantics as tsapiselectagg(...).
select * from tsapiselectaggdata(
'public',
'measurements_1h',
'BB_ar252_1',
'',
'',
100
);
tsapiselectaggwheretsin(
schema_ text,
table_ text,
source_ text,
tsin_ text,
limit_ integer
)
Returns aggregation rows whose timestamps are contained in the provided IN (...) expression text.
select * from tsapiselectaggwheretsin(
'public',
'measurements_1h',
'BB_ar252_1',
'(''2026-04-21T10:00:00Z'',''2026-04-21T11:00:00Z'')',
10
);
tsapiselectvariablefromagg(
schema_ text,
table_ text,
source_ text,
tsfrom_ text,
tsto_ text,
limit_ integer
) returns table (ts timestamp with time zone, data jsonb, extras jsonb)
Returns aggregation rows using the same observable signature and query shape as tsapiselectagg(...) in the attached script. The visible SQL snippet shows it selecting ts, data, and extras for a source with optional time bounds and limit.
Same semantics as tsapiselectagg(...).
select * from tsapiselectvariablefromagg(
'public',
'measurements_1h',
'BB_ar252_1',
'',
'',
100
);
select tsapicreatetable('public', 'measurements');
select tsapiinsert(
'public',
'measurements',
'BB_ar252_1',
now(),
'{"t":21.4,"rH":45.0}'::jsonb
);
This is the standard pattern for raw time-series storage.
select * from tsapiselectlast('public', 'measurements', 'BB_ar252_1');
Use this for last-value panels and incremental processing checkpoints.
select tsapicreateaggregationtable('public', 'measurements_1h');
select tsapiinsertagg(
'public',
'measurements_1h',
'BB_ar252_1',
'[
{
"ts":"2026-04-21T10:00:00Z",
"data":{"avg":21.4},
"extras":{"status":"ok"}
}
]'::json
);
select * from tsapiselectaggdata(
'public',
'measurements_1h',
'BB_ar252_1',
'',
'',
100
);
This is the standard aggregation-table workflow.
When generating SQL that uses TSAPI functions, follow these rules:
The aggregator configuration parses period strings and maps them into three groups:
The defaults from JSAggregatorCfg are:
So the default aggregation cascade is equivalent to:
The aggregator creates a cascade of aggregation tables derived from the source table. The manual states that table names are based on the source table plus interval suffixes such as:
The constructor of JSAggregator confirms that it automatically creates aggregation tables for every configured minute/hour/day interval and builds a cascade from the raw table into those derived tables.
TsApi accepts a time zone string for the aggregator. In JSAggregatorCfg, the string is converted into QTimeZone. If the provided time zone is invalid, the runtime falls back to UTC. The time zone is especially relevant for day-based aggregation boundaries.
The aggregator uses stepSize to limit how much data is processed in one aggregation cycle. The manual describes this as the number of source rows handled per aggregation cycle, with the documented default of 10000. The C++ API also exposes setAggregatorStepSize(...) for runtime tuning.
An aggregator may process:
The config parser reads sourcesJsonArray into sourcesList. If the array is empty, aggregation is not restricted to named sources. If it contains values, only those sources are aggregated.
The manual says debug mode shows computation time and SQL insert time for each aggregated source and table. The C++ API exposes setAggregatorDebugMode(...), which toggles the internal mDebugMode flag of the selected aggregator.
TsApi.aggregate(aggregatorName: String): void
Triggers one aggregation cycle for a previously defined aggregator.
This function is intended to be called from onHook(). It tells the internal aggregation engine to process the next portion of data for the named aggregator. In the C++ implementation, if the aggregator exists, aggregate() is executed on the corresponding JSAggregator instance. If the aggregator name is not defined, the engine throws a RangeError.
function onHook() {
TsApi.aggregate("a");
}
TsApi.defineAggregator(
aggregatorName: String,
serverName: String,
sourceTableName: String
): void
TsApi.defineAggregator(
aggregatorName: String,
serverName: String,
sourceTableName: String,
timeZone: String
): void
TsApi.defineAggregator(
aggregatorName: String,
serverName: String,
sourceTableName: String,
timeZone: String,
sourcesJsonArray: String
): void
TsApi.defineAggregator(
aggregatorName: String,
serverName: String,
sourceTableName: String,
timeZone: String,
sourcesJsonArray: String,
periodsJsonArray: String
): void
TsApi.defineAggregator(
aggregatorName: String,
serverName: String,
sourceTableName: String,
timeZone: String,
sourcesJsonArray: String,
periodsJsonArray: String,
stepSize: Number
): void
TsApi.defineAggregator(
aggregatorName: String,
cfg :JSonObject{
"serverName":"sqlServer",
"sourceTable":"raw_table",
"aggregationTable":"aggtable",
"timeZone":"Europe/Warsaw",
"sources":["source"],
"stepSize":10000,
"debugMode":true
}
): void
Creates and registers a named aggregation engine.
This is the central TsApi configuration function. It binds an aggregator name to:
"public.weather"
The source table is expected to follow the TSAPI raw table standard:
(source text, ts timestamp with time zone, data jsonb)
Examples:
"UTC"
"Europe/Warsaw"
"America/Chicago"
If invalid:
Examples:
"[]"
'["SENSOR_1","SENSOR_2"]'
Behavior:
Supported format:
Only strings ending with:
are parsed into minute/hour/day period buckets.
Default behavior from JSAggregatorCfg:
The code exposes this field in the configuration object and manual documents default usage around 10000 rows per cycle.
When an aggregator is defined:
InDriver.import("TsApi");
TsApi.defineAggregator("a", "azureserver", "public.weather");
TsApi.defineAggregator(
"weatherAgg",
"azureserver",
"public.weather",
"Europe/Warsaw"
);
TsApi.defineAggregator(
"weatherAgg",
"azureserver",
"public.weather",
"UTC",
'["Krakow","Chicago"]'
);
TsApi.defineAggregator(
"weatherAgg",
"azureserver",
"public.weather",
"UTC",
'["Krakow","Chicago"]',
'["1m","5m","1h","1d"]'
);
TsApi.defineAggregator(
"weatherAgg",
"azureserver",
"public.weather",
"UTC",
'["Krakow","Chicago"]',
'["1m","5m","1h","1d"]',
20000
);
TsApi.setAggregatorDebugMode(aggregatorName: String, mode: Boolean): void
Enables or disables debug mode for a selected aggregator.
The manual describes debug mode as exposing timing/debug information for calculations and SQL insert operations performed by the aggregator. The C++ implementation sets an internal mDebugMode flag on the matching aggregator if it exists. If the name is missing, nothing happens.
TsApi.setAggregatorDebugMode("weatherAgg", true)
Rules
TsApi.setAggregatorStepSize(aggregatorName: String, stepSize: Number): void
Updates the step size for an existing aggregator.
Step size controls how much data the aggregation engine processes during one TsApi.aggregate(...) cycle. This is useful for tuning throughput versus execution time. The C++ implementation updates the internal step size only if the aggregator exists.
TsApi.setAggregatorStepSize("weatherAgg", 5000);
onStartup:
InDriver.import("TsApi");
TsApi.defineAggregator(
"weatherAgg",
"azureserver",
"public.weather",
"UTC",
"[]",
'["1m","15m","1h","1d"]',
10000
);
TsApi.setAggregatorDebugMode("weatherAgg", true);
InDriver.installHook(60000);
onHook:
TsApi.aggregate("weatherAgg");
This pattern matches the intended TsApi workflow: define once during startup, then trigger incremental aggregation from onHook().
When generating code using TsApi, follow these rules:
The code and SQL together show that TsApi is not just a wrapper around one SQL function. It is a coordinated system:
That structure is exactly why TsApi is well suited for AI-generated code: it has a stable data contract, a fixed aggregation model, and clearly defined configuration parameters.
ProcessApi provides process lifecycle management for external programs in InDriver.
It allows:
The API is stateful — processes are stored internally under a unique name.
Processes are managed by name:
ProcessApi.start("myProcess", "python", ["script.py"]);
Then controlled via:
ProcessApi.waitForFinished("myProcess");
ProcessApi.kill("myProcess");
ProcessApi.start(name: String, program: String, args: Array): String
Starts a new process and registers it under a given name.
name
program
args
String
Meaning
ProcessApi.start("job1", "python", ["script.py"]);
ProcessApi.close(name: String): void
Gracefully closes a running process.
ProcessApi.close("job1");
ProcessApi.closeAll(): void
Gracefully closes all managed processes.
ProcessApi.kill(name: String): void
Forcefully terminates a process.
ProcessApi.kill("job1");
ProcessApi.killAll(): void
Forcefully terminates all processes.
ProcessApi.waitForStarted(name: String, msecs: Number = 30000): Boolean
Waits until a process starts.
name
msecs
Boolean
ProcessApi.start("job1", "python", ["script.py"]);
if (!ProcessApi.waitForStarted("job1")) {
InDriver.debug("Process failed to start", "critical");
}
ProcessApi.waitForFinished(name: String, msecs: Number = 30000): Boolean
Waits until a process finishes.
ProcessApi.waitForFinished("job1", 10000);
ProcessApi.waitAllForStarted(msecs: Number = 30000): Boolean
Waits until all processes are started.
ProcessApi.waitAllForFinished(msecs: Number = 30000): Boolean
Waits until all processes are finished.
ProcessApi.setWorkingDirectory(name: String, dir: String): void
Sets working directory for a process before start.
ProcessApi.setWorkingDirectory("job1", "c:/scripts");
ProcessApi.workingDirectory(name: String): String
Returns working directory of the process.
ProcessApi.program(name: String): String
Returns program path used to start the process.
ProcessApi.pid(name: String): Number
Returns operating system process ID.
let pid = ProcessApi.pid("job1");
ProcessApi.state(name: String): String
Returns current process state.
NotRunning
Starting
Running
Finished
ProcessApi.status(name: String): Object
Returns detailed process status.
Object
{
state: "Running",
pid: 1234,
program: "python",
workingDirectory: "c:/scripts"
}
ProcessApi.remove(name: String): void
Removes process from internal registry.
ProcessApi.start("job1", "python", ["script.py"]);
if (ProcessApi.waitForFinished("job1")) {
InDriver.debug("Done");
} else {
InDriver.debug("Timeout", "critical");
}
ProcessApi.start("job1", "python", ["a.py"]);
ProcessApi.start("job2", "python", ["b.py"]);
ProcessApi.waitAllForFinished();
if (ProcessApi.state("job1") !== "Running") {
ProcessApi.start("job1", "service.exe", []);
}
When generating code using ProcessApi, follow these rules:
"job1", "collector", "analyzer"
waitForStarted()
waitForFinished(name, timeout)
kill(name)
remove(name)
PdfApi provides PDF reading and rendering functionality for JavaScript tasks in InDriver.
It allows:
The API is stateful: all functions operate on the currently loaded PDF document.
PdfApi.load(file: String): String
Loads a PDF document from disk.
String
Possible return values:
InDriver.import("PdfApi");
let status = PdfApi.load(InDriver.currentPath() + "/document.pdf");
InDriver.debug("Load status: " + status);
PdfApi.isLoaded(): Boolean
Checks whether a PDF document is currently loaded and ready.
Boolean
if (!PdfApi.isLoaded()) {
InDriver.debug("PDF is not loaded", "critical");
}
PdfApi.pageText(page: Number): String
Returns the extracted text of a single PDF page.
String
let page0 = PdfApi.pageText(0);
InDriver.debug(page0);
PdfApi.readText(from: Number = 0, count: Number = -1): String
Returns concatenated text from a page range of the currently loaded PDF.
String
let allText = PdfApi.readText();
let fromPage5 = PdfApi.readText(5);
let intro = PdfApi.readText(0, 2);
InDriver.import("PdfApi");
PdfApi.load(InDriver.currentPath() + "/manual.pdf");
let intro = PdfApi.readText(0, 2);
InDriver.debug("Intro text:\n" + intro);
PdfApi.pageLabel(page: Number): String
Returns the label of a specific page.
Page labels may differ from numeric page indexes in PDFs that use custom numbering schemes.
String
let label = PdfApi.pageLabel(0);
InDriver.debug("Page Label: " + label);
PdfApi.allPageLabels(): String[]
Returns labels of all pages in the currently loaded PDF.
String[]
let labels = PdfApi.allPageLabels();
InDriver.debug(labels);
PdfApi.setCodec(codec: String): Boolean
Sets the codec used for text decoding during PDF text extraction.
Boolean
PdfApi.setCodec("Utf8");
InDriver.debug("Codec set to Utf8");
PdfApi.pageCount(): Number
Returns the total number of pages in the loaded PDF document.
Number
let count = PdfApi.pageCount();
InDriver.debug("Total Pages: " + count);
PdfApi.setPassword(password: String): void
Sets the password used to open a password-protected PDF.
PdfApi.setPassword("securePassword123");
let status = PdfApi.load(InDriver.currentPath() + "/protected.pdf");
InDriver.debug("Load status: " + status);
PdfApi.password(): String
Returns the password currently stored in the PDF session.
String
let pwd = PdfApi.password();
InDriver.debug("Current Password: " + pwd);
PdfApi.status(): String
Returns the current document status.
String
Possible values:
let status = PdfApi.status();
InDriver.debug("Pdf status: " + status);
PdfApi.metadata(): String
Returns document metadata as serialized JSON text.
String
The returned JSON includes at least:
let meta = PdfApi.metadata();
InDriver.debug(meta);
let meta = JSON.parse(PdfApi.metadata());
PdfApi.asImageBase64(page: Number, format: String = "PNG"): String
Renders a PDF page as an image and returns it as a Base64 data URL.
String
data:image/png;base64,...
let img = PdfApi.asImageBase64(0);
InDriver.debug(img);
PdfApi.savePageAsImage(page: Number, path: String): String
Renders the specified PDF page and saves it as an image file.
String
PdfApi.load(InDriver.currentPath() + "/report.pdf");
let out = PdfApi.savePageAsImage(0, InDriver.currentPath() + "/page0.png");
InDriver.debug("Saved image: " + out);
PdfApi.close(): void
Closes the currently loaded PDF document.
PdfApi.close();
onStartup:
InDriver.import("PdfApi");
PdfApi.setCodec("Utf8");
let status = PdfApi.load(InDriver.currentPath() + "/manual.pdf");
if (status !== "OK") {
InDriver.debug("PDF load failed: " + status, "critical");
return;
}
InDriver.debug("Pages: " + PdfApi.pageCount());
InDriver.debug("Metadata: " + PdfApi.metadata());
let intro = PdfApi.readText(0, 2);
InDriver.debug(intro);
let img = PdfApi.savePageAsImage(0, InDriver.currentPath() + "/page0.png");
InDriver.debug("Saved image: " + img);
PdfApi.close();
When generating code using PdfApi, follow these rules:
InDriver.import("PdfApi");
let status = PdfApi.load(path);
if (status !== "OK") { ... }
FileApi provides file and directory operations for JavaScript tasks in InDriver.
It supports:
Files are managed using a handle name (name), not directly by path after opening.
FileApi.addFileSystemWatcherPath(file: String): Boolean
Adds a file or directory to the filesystem watcher.
FileApi.addFileSystemWatcherPath("c:/data/config.json");
FileApi.appendLine(name: String, line: String): Boolean
Appends a single line to an opened file.
FileApi.open("log", "c:/log.txt", ["Append"]);
FileApi.appendLine("log", "New event");
FileApi.close(name: String): void
Closes an opened file.
FileApi.close("log");
FileApi.closeAll(): void
Closes all opened files.
FileApi.copy(from: String, to: String): Boolean
Copies a file from one location to another.
FileApi.copy("c:/a.txt", "c:/backup/a.txt");
FileApi.dirGoTo(dir: String): Boolean
Changes current working directory to a subdirectory.
FileApi.dirGoToApplicationDirectory(): Boolean
Sets working directory to InDriver application directory.
FileApi.dirGoToPath(path: String): Boolean
Changes working directory to an absolute path.
FileApi.dirGoUp(): Boolean
Moves working directory one level up.
FileApi.dirList(fileTypeFilter: String = ""): String
Returns directory content as serialized string.
let list = FileApi.dirList("*.txt");
InDriver.debug(list);
FileApi.dirLoadFiles(files: String): String
Loads multiple files based on input definition.
FileApi.dirFindFiles(filter: Object): Object
Searches for files in a directory using multiple filter conditions.
The function can filter files by:
The function returns a structured object containing matching files and search metadata.
This function is useful for automatic file processing tasks, for example:
filter definition object
Supported filter fields
optional
Directory path to search.
If not specified, the current FileApi browser directory is used.
Example
path: "C:/Data/Reports"
optional
List of allowed file extensions.
Extensions can be written with or without leading dot.
Example
extensions: ["xlsx", "csv"]
or
extensions: [".xlsx", ".csv"]
optional
List of wildcard file name filters.
Example
nameFilters: ["*.xlsx", "report_*.csv"]
optional
Regular expression matched against the file name.
Example
namePattern: "report_.*\\.xlsx"
optional
Minimum file size in bytes.
Example
minSize: 1000
optional
Maximum file size in bytes.
Example
maxSize: 10485760
optional
Minimum file creation date/time (Local Time).
Format: ISO date/time string.
Example
createdFrom: "2026-06-10T00:00:00"
optional
Maximum file creation date/time (Local Time).
Format: ISO date/time string.
Example
createdTo: "2026-06-10T23:59:59"
optional
Minimum file modification date/time.
Format: ISO date/time string.
Example
modifiedFrom: "2026-06-10T00:00:00"
optional
Maximum file modification date/time.
Format: ISO date/time string.
Example
modifiedTo: "2026-06-10T23:59:59"
optional
If true, subdirectories are scanned recursively.
Default: false
optional
If true, extension and regular expression matching is case-sensitive.
Default: false
optional
If true, the API checks whether each matching file appears to be a text file.
Default: false
Remark:
This option opens and reads the first bytes of every matching file, so it should be disabled for very large searches unless needed.
optional
Maximum number of matching files returned.
If the limit is less than or equal to 0, all matching files are returned.
Object
The returned object contains:
Always:
"FileFinderResult"
Searched directory path.
true if search was executed successfully.
false if search failed.
Error message, if search failed.
Number of returned matching files.
Number of scanned directory entries.
true if the result was stopped because the limit was reached.
Array of matching file objects.
Each file object contains:
Example result
{
"type": "FileFinderResult",
"path": "C:/Data/Reports",
"ok": true,
"count": 2,
"scanned": 125,
"limited": false,
"files": [
{
"name": "report_001.xlsx",
"fileName": "report_001.xlsx",
"path": "C:/Data/Reports",
"filePath": "C:/Data/Reports/report_001.xlsx",
"absoluteFilePath": "C:/Data/Reports/report_001.xlsx",
"absolutePath": "C:/Data/Reports",
"suffix": "xlsx",
"baseName": "report_001",
"completeBaseName": "report_001",
"size": "34890",
"created": "2026-06-10T08:15:00",
"modified": "2026-06-10T08:16:10",
"metadataChanged": "2026-06-10T08:16:10",
"lastRead": "2026-06-10T08:20:00"
}
]
}
Find XLSX files modified today
let result = FileApi.dirFindFiles({
path: "C:/Data/Reports",
extensions: ["xlsx"],
modifiedFrom: "2026-06-10T00:00:00",
recursive: false,
limit: 100
});
if (!result.ok) {
InDriver.debug(result.error, "critical");
return;
}
for (let i = 0; i < result.files.length; i++) {
let file = result.files[i];
InDriver.debug(file.absoluteFilePath);
}
Find Excel reports by regular expression
let result = FileApi.dirFindFiles({
path: "C:/Data/Reports",
extensions: ["xlsx"],
namePattern: "report_.*\\.xlsx",
modifiedFrom: "2026-06-01T00:00:00",
minSize: 1000,
maxSize: 10485760,
limit: 500
});
for (let i = 0; i < result.files.length; i++) {
InDriver.debug(result.files[i].absoluteFilePath);
}
Find files and parse XLSX reports
InDriver.import("FileApi");
InDriver.import("XlsxApi");
let found = FileApi.dirFindFiles({
path: "C:/Data/Incoming",
extensions: ["xlsx"],
modifiedFrom: "2026-06-10T00:00:00",
recursive: false,
limit: 100
});
if (!found.ok) {
InDriver.debug(found.error, "critical");
return;
}
for (let i = 0; i < found.files.length; i++) {
let file = found.files[i].absoluteFilePath;
if (!XlsxApi.open("report", file)) {
InDriver.debug("Cannot open XLSX: " + XlsxApi.lastError(), "critical");
continue;
}
let rows = XlsxApi.table("report", "Sheet1", 1, 2);
InDriver.debug("Rows: " + rows.length);
XlsxApi.close("report");
}
This prevents the incoming directory from growing indefinitely.
When generating code using FileApi.dirFindFiles, follow these rules:
FileApi.dirSaveFiles(filesArray: String): String
Saves multiple files based on structured input.
FileApi.fileExists(path: String): Boolean
Checks whether a file exists.
if (FileApi.fileExists("c:/data.txt")) {
InDriver.debug("File exists");
}
FileApi.fileSize(name: String): Number
Returns size of an opened file in bytes.
FileApi.isOpen(name: String): Boolean
Checks whether a file handle is currently open.
FileApi.open(name: String, file: String, modes: Array = ["ReadOnly"]): String
Opens a file and assigns it to a handle.
name
file
modes
FileApi.open("f1", "c:/data.txt", ["ReadWrite"]);
FileApi.readAll(name: String): String
Reads entire content of an opened file.
FileApi.readAllBase64(name: String): String
Reads file and returns Base64 encoded content.
FileApi.readLines(name: String): Array
Reads file and returns array of lines.
let lines = FileApi.readLines("f1");
FileApi.removeFile(path: String): Boolean
Deletes a file from disk.
FileApi.removeFileSystemWatcherPath(file: String): Boolean
Removes file or directory from watcher.
FileApi.write(name: String, data: String): Number
Writes data to an opened file.
FileApi.write("f1", "Hello World");
onStartup:
InDriver.import("FileApi");
FileApi.open("log", "c:/log.txt", ["Append"]);
if (FileApi.isOpen("log")) {
FileApi.appendLine("log", "Application started");
}
FileApi.open("f1", "c:/data.txt", ["ReadOnly"]);
let content = FileApi.readAll("f1");
InDriver.debug(content);
FileApi.close("f1");
let data = InDriver.sqlExecute("local", "select * from table");
FileApi.open("out", "c:/export.json", ["WriteOnly"]);
FileApi.write("out", JSON.stringify(data));
FileApi.close("out");
When generating code using FileApi, follow these rules:
FileApi.open(name, path, modes)
if (!FileApi.isOpen(name)) { ... }
FileApi.close(name)
FileApi.appendLine(...)
FileApi.readAllBase64(...)
FileApi.fileExists(path)
SerialPortApi provides serial communication (RS232 / RS485) for JavaScript tasks in InDriver.
It allows:
Ports are managed using a handle name (name).
SerialPortApi.availablePorts(): Array
Returns a list of available serial ports on the system.
Array
let ports = SerialPortApi.availablePorts();
InDriver.debug(ports);
SerialPortApi.close(name: String): void
Closes an opened serial port.
SerialPortApi.close("port1");
SerialPortApi.closeAll(): void
Closes all opened serial ports.
SerialPortApi.flush(name: String): void
Clears input and output buffers of the serial port.
SerialPortApi.isOpen(name: String): Boolean
Checks whether a serial port is open.
SerialPortApi.open(name: String, port: String, config: Object): Boolean
Opens and configures a serial port.
name
port
config
{
baudRate: Number,
dataBits: Number,
parity: "None" | "Even" | "Odd",
stopBits: Number,
flowControl: "None" | "Hardware" | "Software"
}
SerialPortApi.open("p1", "COM3", {
baudRate: 9600,
dataBits: 8,
parity: "None",
stopBits: 1,
flowControl: "None"
});
SerialPortApi.read(name: String): String
Reads available data from serial port buffer.
SerialPortApi.readAll(name: String): String
Reads entire buffer content.
SerialPortApi.readLine(name: String): String
Reads a single line from the serial port.
SerialPortApi.setTimeout(name: String, msecs: Number): void
Sets read timeout.
SerialPortApi.write(name: String, data: String): Number
Writes data to serial port.
SerialPortApi.write("p1", "AT\r\n");
onStartup:
InDriver.import("SerialPortApi");
SerialPortApi.open("p1", "COM3", {
baudRate: 9600,
dataBits: 8,
parity: "None",
stopBits: 1,
flowControl: "None"
});
onHook
let response = SerialPortApi.readAll("p1");
if (response) {
InDriver.debug(response);
}
SerialPortApi.write("p1", "READ\n");
let response = SerialPortApi.readLine("p1");
InDriver.debug(response);
When generating code using SerialPortApi:
SerialPortApi.open(name, port, config)
SerialPortApi.isOpen(name)
SerialPortApi.close(name)
TcpSocketApi provides client-side TCP socket communication for JavaScript tasks in InDriver.
It allows:
Sockets are managed by a unique name.
TcpSocketApi.acceptRead(name: String): void
Confirms that received data should be treated as an accepted response and that waiting for data may stop.
This function is especially important when writeAndWait(...) is used. In the documented request/response pattern, incoming data arrives through onMessage, and acceptRead(...) is called after the user script decides that the received bytes constitute the expected answer.
function onMessage()
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (JSON.stringify(data.bytes) === "[1,2,3]") {
TcpSocketApi.acceptRead(data.socketName);
}
}
TcpSocketApi.connect(name: String, tcpCfg: String = ""): Boolean
TcpSocketApi.connect(name: String, tcpCfg: Object): Boolean
Creates or recreates a named TCP client socket connection.
If a socket with the same name already exists, it is closed and opened again with the new configuration.
Supported configuration keys visible from the attached files and manual:
From the attached files, if fields are omitted:
Boolean
InDriver.import("TcpSocketApi");
let ok = TcpSocketApi.connect("socket", {
address: "127.0.0.1",
port: 50000,
mode: "Buffer",
timeout: 5000,
bufferSize: 256
});
if (!ok) {
InDriver.debug("TCP socket connection failed", "critical");
}
TcpSocketApi.disconnect(name: String): void
Disconnects the named TCP socket.
TcpSocketApi.disconnect("socket");
TcpSocketApi.disconnectAll(): void
Disconnects all registered TCP sockets.
TcpSocketApi.disconnectAll();
TcpSocketApi.isBusy(name: String): Boolean
Returns whether the socket is currently busy, typically because a request is still being processed or waiting for completion.
Boolean
if (!TcpSocketApi.isBusy("socket")) {
TcpSocketApi.write("socket", [0x01, 0x02, 0x03]);
}
TcpSocketApi.write(
name: String,
data: ByteArray,
request: String = "",
timeout: Number = 0
): Boolean
Writes data to the specified TCP socket.
name
data
Typical JavaScript usage:
[0x01, 0x02, 0x03]
request
timeout
Boolean
TcpSocketApi.write("socket", [0x01, 0x02, 0x03]);
TcpSocketApi.writeAndWait(
name: String,
data: ByteArray,
request: String = "",
timeout: Number = 0
): Boolean
Writes data to the specified TCP socket and waits for a response until timeout or until the incoming data is explicitly accepted.
This is a blocking request/response helper. The waiting ends when:
name
data
request
timeout
Boolean
onStartup:
InDriver.import("TcpSocketApi");
TcpSocketApi.connect("socket", '{"address":"127.0.0.1","port":50000}');
onHook:
TcpSocketApi.writeAndWait("socket", [0x01, 0x02, 0x03], "req1", 5000);
onMessage:
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (JSON.stringify(data.bytes) === "[1,2,3]") {
TcpSocketApi.acceptRead(data.socketName);
}
}
When data is received on a TCP socket, the task onMessage() handler is triggered.
In this case:
let d = JSON.parse(InDriver.messageData());
/*
d =
{
"bytes": "[79,75]",
"data": "OK",
"request": "request name",
"socketName": "name of socket"
}
*/
onStartup:
InDriver.import("TcpSocketApi");
TcpSocketApi.connect("socket", {
address: "127.0.0.1",
port: 50000,
mode: "Buffer",
timeout: 5000,
bufferSize: 256
});
onHook:
TcpSocketApi.writeAndWait("socket", [0x01, 0x02, 0x03], "req1", 5000);
onMessage:
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (JSON.stringify(data.bytes) === "[1,2,3]") {
TcpSocketApi.acceptRead(data.socketName);
}
}
TcpServerApi provides TCP server functionality for JavaScript tasks in InDriver.
It allows:
The current documented API surface contains:
TcpSocketApi.close(name: String): void
Closes and removes the TCP socket instance associated with the given name.
Unlike disconnect(...), which only closes the connection, close(...):
TcpSocketApi.close("socket");
onStartup:
InDriver.import("TcpSocketApi");
TcpSocketApi.connect("socket", {
address: "127.0.0.1",
port: 50000
});
onShutdown:
TcpSocketApi.close("socket");
Function | Behavior |
disconnect(name) | closes connection but keeps socket object |
close(name) | closes connection AND removes socket |
When generating code:
onShutdown()
TcpServerApi.listen(tcpServerCfg: String): void
Starts a TCP server and listens for incoming connections.
Supported configuration keys from the manual:
InDriver.import("TcpServerApi");
TcpServerApi.listen(
'{"port":50000,"address":"Any","readMode":"Buffer","bufferSize":256}'
);
TcpServerApi.write(name: String, data: ByteArray, timeout: Number): Boolean
Writes data to the TCP client socket associated with the given connection name.
The manual describes this as sending a reply to a connected socket and waiting until the answer is accepted or timeout occurs, with behavior depending on the configured server read mode.
Boolean
onMessage:
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (JSON.stringify(data.bytes) === "[1,2,3]") {
TcpServerApi.write(data.socketName, "bytes accepted", 5000);
}
if (data.data === "text data") {
TcpServerApi.write(data.socketName, "text data accepted", 5000);
}
}
When the server receives data from a connected client, the task onMessage() handler is triggered.
The payload structure follows the same documented pattern as TCP socket reads:
let d = JSON.parse(InDriver.messageData());
/*
d =
{
"bytes": "[79,75]",
"data": "OK",
"request": "request name",
"socketName": "name of socket"
}
*/
onStartup
InDriver.import("TcpSocketApi");
TcpSocketApi.connect("socket", {
address: "127.0.0.1",
port: 50000,
mode: "Buffer",
timeout: 5000,
bufferSize: 256
});
onHook
TcpSocketApi.writeAndWait("socket", [0x01, 0x02, 0x03], "req1", 5000);
onMessage
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (JSON.stringify(data.bytes) === "[1,2,3]") {
TcpSocketApi.acceptRead(data.socketName);
}
}
onStartup
InDriver.import("TcpServerApi");
TcpServerApi.listen('{"port":50000,"address":"Any","mode":"Buffer","bufferSize":256}');
onMessage
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
TcpServerApi.write(data.socketName, "accepted", 5000);
}
When generating code using TcpSocketApi and TcpServerApi, follow these rules:
InDriver.import("TcpSocketApi");
InDriver.import("TcpServerApi");
let data = JSON.parse(InDriver.messageData());
UdpSocketApi provides UDP socket communication for JavaScript tasks in InDriver.
It allows:
Sockets are managed by a unique name.
UdpSocketApi.acceptRead(name: String): void
Marks the current incoming datagram as accepted for the specified socket.
This function is mainly used together with UdpSocketApi.writeAndWait(...). The waiting operation can complete when the script decides that the received response is the expected one and explicitly calls acceptRead(...).
onMessage:
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (data.socketName === "udp1" && data.data === "OK") {
UdpSocketApi.acceptRead("udp1");
}
}
UdpSocketApi.bind(name: String, udpCfg: String = ""): Boolean
UdpSocketApi.bind(name: String, udpCfg: Object): Boolean
Creates or recreates a named UDP socket and binds it to a local address and port.
If a socket with the same name already exists, it is closed and reopened with the new configuration.
Supported keys visible in the source:
Boolean
InDriver.import("UdpSocketApi");
let ok = UdpSocketApi.bind("udp1", {
address: "0.0.0.0",
port: 50000,
timeout: 5000
});
if (!ok) {
InDriver.debug("UDP bind failed", "critical");
}
UdpSocketApi.isBusy(name: String): Boolean
Returns whether the specified UDP socket is currently busy.
Boolean
if (!UdpSocketApi.isBusy("udp1")) {
UdpSocketApi.write("udp1", "192.168.0.10", 6000, [0x01, 0x02], "req1", 5000);
}
UdpSocketApi.write(
name: String,
address: String,
port: Number,
data: ByteArray,
request: String = "",
timeout: Number = 0
): Boolean
Sends a UDP datagram to the specified remote address and port.
Typical JavaScript usage:
[0x01, 0x02, 0x03]
Boolean
UdpSocketApi.write(
"udp1",
"192.168.0.10",
6000,
[0x01, 0x02, 0x03],
"ping1",
5000
);
UdpSocketApi.writeAndWait(
name: String,
address: String,
port: Number,
data: ByteArray,
request: String = "",
timeout: Number = 0
): Boolean
Sends a UDP datagram and waits until a matching response is accepted or the timeout expires.
Boolean
onHook
UdpSocketApi.writeAndWait(
"udp1",
"192.168.0.10",
6000,
[0x10, 0x20],
"req1",
5000
);
onMessage
if (InDriver.taskName() === InDriver.messageSender()) {
let data = JSON.parse(InDriver.messageData());
if (data.socketName === "udp1" && data.data === "OK") {
UdpSocketApi.acceptRead("udp1");
}
}
When a UDP datagram is received, onMessage() is triggered. The source files show that the runtime sends a direct internal message with tag "SockedData" and inserts the last request id under key "request" if available.
The incoming payload should be treated as JSON text available through:
let data = JSON.parse(InDriver.messageData());
Typical fields include:
WebSocketApi provides client-side WebSocket communication for JavaScript tasks in InDriver.
It allows:
● opening named WebSocket connections
● sending text and binary messages
● synchronous request/response communication
● asynchronous message handling via onMessage
● connection state monitoring
WebSocketApi.abort(name: String): void
Immediately aborts the WebSocket connection.
Unlike close(), abort terminates the connection without a closing handshake.
name
● type: String
● socket name
● void
WebSocketApi.acceptRead(name: String): void
Marks the current incoming message as accepted.
Used in request/response patterns together with writeAndWait().
name
● type: String
● void
WebSocketApi.close(name: String, closeCode: Number = 1000, reason: String = ""): void
Closes the WebSocket connection with a proper closing handshake.
name
● type: String
closeCode
● type: Number
● default: 1000
reason
● type: String
● void
WebSocketApi.connect(name: String, wsCfg: String = ""): Boolean
WebSocketApi.connect(name: String, wsCfg: Object): Boolean
Creates or recreates a named WebSocket connection.
name
● type: String
wsCfg
● type: String | Object
Supported fields:
● url
● address / host
● port
● path
● ssl / secure
● origin
● protocol
● headers
● timeout
● waitForConnected
Boolean
● true → success
● false → failure
WebSocketApi.connect("ws", {
url: "ws://127.0.0.1:8081",
waitForConnected: true
});
WebSocketApi.disconnect(name: String): void
Closes a specific WebSocket connection.
WebSocketApi.disconnectAll(): void
Closes all WebSocket connections.
WebSocketApi.errorString(name: String): String
Returns last error message for the socket.
String
WebSocketApi.isBusy(name: String): Boolean
Returns whether the socket is currently busy.
Boolean
WebSocketApi.isValid(name: String): Boolean
Returns whether the socket is valid.
Boolean
WebSocketApi.ping(name: String, payload: String = ""): void
Sends a ping frame.
payload
● optional data
WebSocketApi.state(name: String): String
Returns socket state.
● UnconnectedState
● ConnectingState
● ConnectedState
● ClosingState
WebSocketApi.write(name: String, data: String, request: String = "", timeout: Number = 0): Boolean
Sends a text message.
Boolean
WebSocketApi.write("ws", "hello");
WebSocketApi.writeAndWait(name: String, data: String, request: String = "", timeout: Number = 30000): Boolean
Sends a message and waits for response.
Boolean
● true → response received
● false → timeout
WebSocketApi.writeBinary(name: String, data: ByteArray, request: String = "", timeout: Number = 0): Boolean
Sends binary data.
WebSocketApi.writeBinaryAndWait(name: String, data: ByteArray, request: String = "", timeout: Number = 30000): Boolean
Sends binary data and waits for response.
Incoming messages:
type: "WebSocketData"
Structure:
data.socketName
data.type // text | binary
data.data
data.base64
data.bytes
data.request
Events:
type: "WebSocketEvent"
connected
disconnected
error
pong
● Use write() for async communication
● Use writeAndWait() for request-response
● Use acceptRead() when matching response
● Always import API in onStartup
● Prefer JSON for structured data
WebSocketServerApi provides server-side WebSocket communication for JavaScript tasks in InDriver.
It allows:
● listening for incoming WebSocket connections
● managing multiple connected clients
● sending messages to specific clients
● broadcasting messages to all clients
● handling real-time bidirectional communication
Each client connection is identified by a unique socketName.
WebSocketServerApi.clientCount(): Number
Returns the number of currently connected clients.
Number
WebSocketServerApi.clients(): String[]
Returns a list of connected client socket names.
Array of String
WebSocketServerApi.close(): void
Stops the WebSocket server and disconnects all clients.
● void
WebSocketServerApi.disconnectAllClients(): void
Disconnects all currently connected clients.
● void
WebSocketServerApi.disconnectClient(socketName: String): Boolean
Disconnects a specific client.
socketName
● type: String
Boolean
● true → client disconnected
● false → client not found
WebSocketServerApi.hasClient(socketName: String): Boolean
Checks if a client with the given name exists.
Boolean
WebSocketServerApi.isListening(): Boolean
Returns whether the WebSocket server is currently listening.
Boolean
WebSocketServerApi.listen(cfg: String | Object): Boolean
Starts the WebSocket server.
cfg
Configuration object or JSON string.
Supported fields:
● address
● port
● type: String
● examples:
○ "Any"
○ "LocalHost"
○ "127.0.0.1"
Default: LocalHost
● type: Number
● default: 8081
Boolean
● true → server started
● false → failed to start
WebSocketServerApi.listen({
address: "Any",
port: 8081
});
● Call listen() once during initialization
● Use "Any" to allow external connections
● Use LocalHost for local-only communication
WebSocketServerApi.serverAddress(): String
Returns the address on which the server is listening.
String
WebSocketServerApi.serverPort(): Number
Returns the listening port.
Number
WebSocketServerApi.write(socketName: String, data: String): Boolean
Sends a text message to a specific client.
socketName
● type: String
data
● type: String
Boolean
● true → sent
● false → failed
WebSocketServerApi.write("ws_1", "hello client");
WebSocketServerApi.writeAll(data: String): Number
Sends a text message to all connected clients.
Number
● number of clients that received the message
WebSocketServerApi.writeAll("broadcast message");
WebSocketServerApi.writeBinary(socketName: String, data: ByteArray): Boolean
Sends binary data to a specific client.
WebSocketServerApi.writeBinaryAll(data: ByteArray): Number
Sends binary data to all clients.
Incoming client messages are delivered via:
type: "WebSocketData"
data.socketName
data.data
data.type // text | binary
data.base64
data.bytes
data.peerAddress
data.peerPort
data.localAddress
data.localPort
Connection events are delivered via:
type: "WebSocketEvent"
connected
disconnected
error
pong
onStartup:
InDriver.import("WebSocketServerApi");
WebSocketServerApi.listen({
address: "Any",
port: 8081
});
onMessage
if (InDriver.isLoopbackMessage()) {
let tags = InDriver.messageTags();
let data = JSON.parse(InDriver.messageData());
InDriver.debug(":)" + InDriver.messageData() + " " + JSON.stringify(data.bytes));
if (tags.indexOf("WebSocketEvent") >= 0) {
InDriver.debug("Event: " + data.event + " " + data.socketName);
}
if (tags.indexOf("WebSocketData") >= 0) {
if (data.type === "text") {
let msg = JSON.parse(data.data);
WebSocketServerApi.write(data.socketName, JSON.stringify({
ok: true,
echo: msg
}));
}
if (data.type === "binary") {
InDriver.debug("Binary data: " + JSON.stringify(data.bytes));
WebSocketServerApi.writeBinary(data.socketName, [5, 6, 7]);
}
}
}
● Use WebSocketServerApi for browser/frontend communication
● Each client has unique socketName
● Use write() for response
● Use writeAll() for broadcast
● Always handle incoming messages in onMessage
● Prefer JSON format for communication
XmlReaderApi provides streaming XML reading functionality for JavaScript tasks in InDriver.
It allows:
The API is stateful and operates on the currently loaded XML reader state.
XmlReaderApi.addData(data: String): void
Adds XML text directly into the XML reader.
XmlReaderApi.clear();
XmlReaderApi.addData("<root><a>1</a></root>");
XmlReaderApi.addExtraNamespaceDeclaration(prefix: String, namespaceUri: String): void
Adds an extra namespace declaration to the XML reader.
XmlReaderApi.addExtraNamespaceDeclaration("x", "http://example.com/ns");
XmlReaderApi.atEnd(): Boolean
Returns whether the XML reader is at the end of the stream.
Boolean
while (!XmlReaderApi.atEnd()) {
XmlReaderApi.readNext();
}
XmlReaderApi.attribute(attr: String): String
Returns the value of the specified attribute for the current element.
String
if (XmlReaderApi.isStartElement()) {
let id = XmlReaderApi.attribute("id");
}
XmlReaderApi.characterOffset(): Number
Returns the character offset of the current parser position.
Number
XmlReaderApi.clear(): void
Clears the XML reader state.
XmlReaderApi.clear();
XmlReaderApi.close(): void
Closes the currently opened XML file.
XmlReaderApi.close();
XmlReaderApi.columnNumber(): Number
Returns the current parser column number.
Number
XmlReaderApi.documentEncoding(): String
Returns the document encoding reported by the XML stream.
String
XmlReaderApi.documentVersion(): String
Returns the XML document version.
String
XmlReaderApi.dtdName(): String
Returns the DTD name of the current document, if present.
String
XmlReaderApi.dtdPublicId(): String
Returns the DTD public identifier.
String
XmlReaderApi.dtdSystemId(): String
Returns the DTD system identifier.
String
XmlReaderApi.entityDeclarations(): String
Returns entity declarations as serialized JSON text.
String
The returned JSON contains objects with fields such as:
let entities = JSON.parse(XmlReaderApi.entityDeclarations());
XmlReaderApi.entityExpansionLimit(): Number
Returns the current entity expansion limit.
Number
XmlReaderApi.hasError(): Boolean
Returns whether the XML reader is currently in an error state.
Boolean
if (XmlReaderApi.hasError()) {
InDriver.debug(XmlReaderApi.lastError(), "critical");
}
XmlReaderApi.hasStandaloneDeclaration(): Boolean
Returns whether the XML document has a standalone declaration.
Boolean
XmlReaderApi.isCDATA(): Boolean
Returns whether the current token is CDATA.
Boolean
XmlReaderApi.isCharacters(): Boolean
Returns whether the current token is character data.
Boolean
XmlReaderApi.isComment(): Boolean
Returns whether the current token is a comment.
Boolean
XmlReaderApi.isDTD(): Boolean
Returns whether the current token is a DTD token.
Boolean
XmlReaderApi.isEndDocument(): Boolean
Returns whether the current token is end-of-document.
Boolean
XmlReaderApi.isEndElement(): Boolean
Returns whether the current token is an end element.
Boolean
XmlReaderApi.isEntityReference(): Boolean
Returns whether the current token is an entity reference.
Boolean
XmlReaderApi.isProcessingInstruction(): Boolean
Returns whether the current token is a processing instruction.
Boolean
XmlReaderApi.isStandaloneDocument(): Boolean
Returns whether the XML document is standalone.
Boolean
XmlReaderApi.isStartDocument(): Boolean
Returns whether the current token is the start of the document.
Boolean
XmlReaderApi.isStartElement(): Boolean
Returns whether the current token is a start element.
Boolean
XmlReaderApi.isWhitespace(): Boolean
Returns whether the current token is whitespace.
Boolean
XmlReaderApi.lastError(): String
Returns the current XML parser error string.
String
if (XmlReaderApi.hasError()) {
InDriver.debug(XmlReaderApi.lastError(), "critical");
}
XmlReaderApi.lineNumber(): Number
Returns the current parser line number.
Number
XmlReaderApi.name(): String
Returns the current element or token name.
String
if (XmlReaderApi.isStartElement()) {
InDriver.debug(XmlReaderApi.name());
}
XmlReaderApi.namespaceProcessing(): Boolean
Returns whether namespace processing is enabled.
Boolean
XmlReaderApi.namespaceUri(): String
Returns the namespace URI of the current token.
String
XmlReaderApi.open(file: String): String
Opens an XML file for reading.
String
let err = XmlReaderApi.open("c:/data/config.xml");
if (err) {
InDriver.debug(err, "critical");
}
XmlReaderApi.prefix(): String
Returns the namespace prefix of the current token.
String
XmlReaderApi.processingInstructionData(): String
Returns the data part of the current processing instruction.
String
XmlReaderApi.processingInstructionTarget(): String
Returns the target part of the current processing instruction.
String
XmlReaderApi.qualifiedName(): String
Returns the qualified name of the current token.
String
XmlReaderApi.readElementText(
behaviour: String = "ErrorOnUnexpectedElement"
): String
Reads and returns the text content of the current element.
String
if (XmlReaderApi.isStartElement() && XmlReaderApi.name() === "title") {
let text = XmlReaderApi.readElementText("SkipChildElements");
InDriver.debug(text);
}
XmlReaderApi.readNext(): String
Advances the XML reader to the next token and returns the token type as string.
String
Possible values include:
while (!XmlReaderApi.atEnd()) {
let token = XmlReaderApi.readNext();
InDriver.debug(token);
}
XmlReaderApi.readNextStartElement(): Boolean
Advances the reader until the next start element is found.
Boolean
while (XmlReaderApi.readNextStartElement()) {
InDriver.debug(XmlReaderApi.name());
}
XmlReaderApi.setEntityExpansionLimit(limit: Number): void
Sets the entity expansion limit of the XML reader.
XmlReaderApi.setNamespaceProcessing(enabled: Boolean): void
Enables or disables namespace processing.
XmlReaderApi.setNamespaceProcessing(true);
XmlReaderApi.skipCurrentElement(): void
Skips the current element, including its child content.
if (XmlReaderApi.isStartElement() && XmlReaderApi.name() === "ignore") {
XmlReaderApi.skipCurrentElement();
}
XmlReaderApi.text(): String
Returns the text of the current token.
String
if (XmlReaderApi.isCharacters()) {
InDriver.debug(XmlReaderApi.text());
}
XmlReaderApi.tokenString(): String
Returns the current token as a string representation provided by the XML reader.
String
XmlReaderApi.tokenType(): String
Returns the current token type as a normalized token name.
String
Possible values include:
onStartup:
InDriver.import("XmlReaderApi");
let err = XmlReaderApi.open("c:/data/config.xml");
if (err) {
InDriver.debug(err, "critical");
return;
}
while (!XmlReaderApi.atEnd()) {
let token = XmlReaderApi.readNext();
if (XmlReaderApi.isStartElement()) {
InDriver.debug("Element: " + XmlReaderApi.name());
}
if (XmlReaderApi.isCharacters()) {
InDriver.debug("Text: " + XmlReaderApi.text());
}
}
if (XmlReaderApi.hasError()) {
InDriver.debug(XmlReaderApi.lastError(), "critical");
}
XmlReaderApi.close();
while (XmlReaderApi.readNextStartElement()) {
if (XmlReaderApi.name() === "title") {
let title = XmlReaderApi.readElementText("SkipChildElements");
InDriver.debug("Title: " + title);
} else {
XmlReaderApi.skipCurrentElement();
}
}
XmlReaderApi.clear();
XmlReaderApi.addData("<root><item id='1'>A</item></root>");
while (!XmlReaderApi.atEnd()) {
XmlReaderApi.readNext();
if (XmlReaderApi.isStartElement()) {
InDriver.debug(XmlReaderApi.name());
InDriver.debug(XmlReaderApi.attribute("id"));
}
}
When generating code using UdpSocketApi and XmlReaderApi, follow these rules:
XlsxApi provides Microsoft Excel XLSX reading and writing functionality for JavaScript tasks in InDriver.
It allows:
The API is stateful. A workbook is opened under a logical name, and all later functions operate on that workbook name.
XlsxApi supports modern Excel XLSX files.
Legacy XLS files are not supported.
XlsxApi.open(name: String, file: String): Boolean
Opens an XLSX workbook from disk and registers it under a logical name.
logical workbook name used in later calls
full or relative path to XLSX file
Boolean
true → workbook was opened successfully
false → opening failed
InDriver.import("XlsxApi");
let ok = XlsxApi.open("report", InDriver.currentPath() + "/report.xlsx");
if (!ok) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}
Always check the returned value.
If open(...) returns false, use XlsxApi.lastError() to read the reason.
Use close(...) when the workbook is no longer needed.
XlsxApi.close(name: String): void
Closes one opened workbook.
logical workbook name
void
XlsxApi.close("report");
Call close(...) after processing the workbook, especially in long-running tasks.
XlsxApi.closeAll(): void
Closes all opened workbooks.
void
XlsxApi.closeAll();
Useful during task shutdown or when reloading many Excel files.
XlsxApi.isOpen(name: String): Boolean
Checks whether a workbook is currently opened under the specified logical name.
logical workbook name
Boolean
true → workbook is open
false → workbook is not open
if (!XlsxApi.isOpen("report")) {
InDriver.debug("Workbook is not open", "critical");
}
XlsxApi.sheets(name: String): Array
Returns the list of worksheet names from the opened workbook.
logical workbook name
Array
array of worksheet names
let sheets = XlsxApi.sheets("report");
InDriver.debug(JSON.stringify(sheets));
Example result
[
"Sheet1",
"Orders",
"Report"
]
Use sheets(...) before selectSheet(...) when the worksheet name may vary between files.
XlsxApi.selectSheet(name: String, sheet: String): Boolean
Selects the active worksheet in the opened workbook.
logical workbook name
worksheet name
Boolean
true → worksheet was selected
false → worksheet was not found or workbook is not open
if (!XlsxApi.selectSheet("report", "Orders")) {
InDriver.debug("Sheet not found", "critical");
}
After selecting a worksheet, read(...), readA1(...), row(...), range(...) and table(...) operate on the selected worksheet unless a function accepts a sheet argument explicitly.
XlsxApi.currentSheet(name: String): String
Returns the name of the currently selected worksheet.
logical workbook name
String
current worksheet name
empty string if workbook is not open or no worksheet is selected
let sheet = XlsxApi.currentSheet("report");
InDriver.debug("Current sheet: " + sheet);
XlsxApi.dimension(name: String, sheet: String = ""): Object
Returns the used range of the selected or specified worksheet.
logical workbook name
optional worksheet name
if empty, the current worksheet is used
Object
The returned object contains:
sheet
firstRow
lastRow
firstCol
lastCol
rowCount
colCount
let dim = XlsxApi.dimension("report");
InDriver.debug(JSON.stringify(dim));
Example result
{
"sheet": "Orders",
"firstRow": 1,
"lastRow": 500,
"firstCol": 1,
"lastCol": 12,
"rowCount": 500,
"colCount": 12
}
Excel row and column indexes start from 1.
Use dimension(...) before manual loops over rows and columns.
XlsxApi.metadata(name: String): Object
Returns basic workbook and file metadata.
logical workbook name
Object
The returned object may contain:
name
file
fileName
fileSize
lastModified
sheetCount
currentSheet
sheets
let meta = XlsxApi.metadata("report");
InDriver.debug(JSON.stringify(meta));
Use metadata(...) for diagnostics, logging and validation before parsing a file.
XlsxApi.read(name: String, row: Number, col: Number): Variant
Reads a cell value by row and column index.
logical workbook name
Excel row index, starting from 1
Excel column index, starting from 1
Variant
cell value
undefined/null-like value if workbook or cell is invalid
let value = XlsxApi.read("report", 2, 5);
InDriver.debug("Value: " + value);
Indexes start from 1.
Use readA1(...) if Excel-style cell references are more convenient.
XlsxApi.readA1(name: String, cell: String): Variant
Reads a cell value using Excel A1 notation.
logical workbook name
Excel cell reference, for example "A1" or "E2"
Variant
cell value
let value = XlsxApi.readA1("report", "E2");
InDriver.debug("Value: " + value);
XlsxApi.exists(name: String, row: Number, col: Number): Boolean
Checks whether a cell has a non-empty value.
logical workbook name
Excel row index, starting from 1
Excel column index, starting from 1
Boolean
true → cell contains a non-empty value
false → cell is empty or invalid
if (XlsxApi.exists("report", 10, 3)) {
InDriver.debug("Cell contains data");
}
XlsxApi.existsA1(name: String, cell: String): Boolean
Checks whether a cell identified by Excel A1 notation has a non-empty value.
logical workbook name
Excel cell reference, for example "A1"
Boolean
if (XlsxApi.existsA1("report", "A10")) {
InDriver.debug("A10 contains data");
}
XlsxApi.cell(name: String, row: Number, col: Number): Object
Returns one cell as an object containing cell metadata and value.
logical workbook name
Excel row index, starting from 1
Excel column index, starting from 1
Object
The returned object contains:
row
col
ref
type
value
let c = XlsxApi.cell("report", 2, 3);
InDriver.debug(JSON.stringify(c));
Example result
{
"row": 2,
"col": 3,
"ref": "C2",
"type": "number",
"value": 123.45
}
XlsxApi.row(name: String, row: Number, firstCol: Number = 1, lastCol: Number = -1): Array
Returns one worksheet row as an array of cell objects.
logical workbook name
Excel row index, starting from 1
optional
first column to read
optional
last column to read
if lastCol is less than 1, the worksheet dimension is used
Array
array of cell objects
Example
let row = XlsxApi.row("report", 5);
InDriver.debug(row);
let selected = XlsxApi.row("report", 5, 1, 10);
InDriver.debug(selected);
Use row(...) for diagnostics or when the row layout is not known in advance.
For table-like data, prefer table(...).
XlsxApi.range(name: String, firstRow: Number, firstCol: Number, lastRow: Number, lastCol: Number): Array
Returns a rectangular worksheet range as a matrix array.
logical workbook name
first row index
first column index
last row index
last column index
Array
array of arrays
let data = XlsxApi.range("report", 1, 1, 10, 5);
InDriver.debug(JSON.stringify(data));
Example result
[
["Date", "Value"],
["2026-01-01", 123.45],
["2026-01-02", 140.10]
]
Use range(...) when data has no headers or when you want matrix-style access.
Use table(...) when the first row contains column names.
XlsxApi.table(name: String,sheet: String = "",headerRow: Number = 1,firstDataRow: Number = 2,firstCol: Number = -1,lastCol: Number = -1,lastRow: Number = -1,skipEmptyRows:Boolean = true): Array
Converts a worksheet table into an array of JavaScript objects.
The header row is used as object property names.
Each data row becomes one object.
logical workbook name
optional
worksheet name
if empty, the current worksheet is used
optional
row containing column headers
optional
first row containing data
optional
first column to read
if less than 1, the worksheet dimension is used
optional
last column to read
if less than 1, the worksheet dimension is used
optional
last row to read
if less than 1, the worksheet dimension is used
optional
if true, empty rows are skipped
Array
array of row objects
let rows = XlsxApi.table("report", "Orders", 1, 2);
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
InDriver.debug(JSON.stringify(row));
}
Example result
[
{
"Order": 1001,
"Product": "A",
"Quantity": 10
},
{
"Order": 1002,
"Product": "B",
"Quantity": 20
}
]
This is the recommended function for parsing standard Excel reports.
Use clear and unique column headers in Excel files.
If a header cell is empty, the API uses the Excel column name such as "A", "B", "C".
XlsxApi.toObject(name: String, options: Object = {}): Object
Converts one worksheet, selected worksheets, or the entire workbook into a structured JavaScript object.
logical workbook name
optional
conversion options
Options:
supported values:
"table"
"matrix"
"cells"
default: "table"
worksheet name
if true, all worksheets are converted
row containing headers for table mode
first data row for table mode
first row to read
last row to read
first column to read
last column to read
if true, empty rows are skipped
if true, table rows include cell reference fields
Object
structured workbook object
let obj = XlsxApi.toObject("report", {
mode: "table",
sheet: "Orders",
headerRow: 1,
firstDataRow: 2,
skipEmptyRows: true
});
InDriver.debug(JSON.stringify(obj));
//Other example
let all = XlsxApi.toObject("report", {
mode: "matrix",
allSheets: true
});
Use toObject(...) when you need a complete structured representation.
For large files, prefer table(...), range(...), or manual row processing.
XlsxApi.findColumn(name: String,header: String,headerRow: Number = 1,sheet: String = "",
caseSensitive: Boolean = false): Number
Finds the column number by header text.
logical workbook name
header text to search for
optional
row containing headers
optional
worksheet name
if empty, current worksheet is used
optional
if true, matching is case-sensitive
Number
column number if found
-1 if not found
let col = XlsxApi.findColumn("report", "Value");
if (col < 0) {
InDriver.debug("Column not found", "critical");
return;
}
let value = XlsxApi.read("report", 2, col);
Use findColumn(...) when Excel reports may change column order.
XlsxApi.write(name: String, row: Number, col: Number, value: Variant): Boolean
Writes a value to a cell identified by row and column index.
logical workbook name
Excel row index, starting from 1
Excel column index, starting from 1
value to write
Boolean
true → value was written
false → writing failed
XlsxApi.write("report", 2, 5, 123.45);
Call save(...) or saveAs(...) to persist changes to disk.
XlsxApi.writeA1(name: String, cell: String, value: Variant): Boolean
Writes a value to a cell identified by Excel A1 notation.
logical workbook name
Excel cell reference, for example "B2"
value to write
Boolean
XlsxApi.writeA1("report", "B2", "Processed");
Call save(...) or saveAs(...) to persist changes to disk.
XlsxApi.save(name: String): Boolean
Saves the workbook to its original file.
logical workbook name
Boolean
true → workbook was saved
false → saving failed
if (!XlsxApi.save("report")) {
InDriver.debug("Save failed", "critical");
}
Use save(...) only when overwriting the original file is expected.
Use saveAs(...) when creating a modified copy.
XlsxApi.saveAs(name: String, file: String): Boolean
Saves the workbook to a new XLSX file.
logical workbook name
output XLSX file path
Boolean
true → workbook was saved
false → saving failed
let out = InDriver.currentPath() + "/report_processed.xlsx";
if (!XlsxApi.saveAs("report", out)) {
InDriver.debug("SaveAs failed", "critical");
}
XlsxApi.lastError(): String
Returns the last error text reported by XlsxApi.
String
if (!XlsxApi.open("report", "missing.xlsx")) {
InDriver.debug(XlsxApi.lastError(), "critical");
}
Use lastError() mainly after open(...) fails.
Recommended usage patterns
Pattern 1 — read XLSX table
onStartup:
InDriver.import("XlsxApi");
let file = InDriver.currentPath() + "/report.xlsx";
if (!XlsxApi.open("report", file)) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}
let rows = XlsxApi.table("report", "Sheet1", 1, 2);
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
InDriver.debug(JSON.stringify(row));
}
XlsxApi.close("report");
Pattern 2 — read selected columns by header name
onStartup:
InDriver.import("XlsxApi");
if (!XlsxApi.open("report", InDriver.currentPath() + "/report.xlsx")) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}
let valueCol = XlsxApi.findColumn("report", "Value");
let timeCol = XlsxApi.findColumn("report", "Timestamp");
if (valueCol < 0 || timeCol < 0) {
InDriver.debug("Required column not found", "critical");
XlsxApi.close("report");
return;
}
let dim = XlsxApi.dimension("report");
for (let r = 2; r <= dim.lastRow; r++) {
let ts = XlsxApi.read("report", r, timeCol);
let value = XlsxApi.read("report", r, valueCol);
InDriver.debug("ts=" + ts + ", value=" + value);
}
XlsxApi.close("report");
Pattern 3 — convert workbook to object
onStartup:
InDriver.import("XlsxApi");
if (!XlsxApi.open("report", InDriver.currentPath() + "/report.xlsx")) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}
let obj = XlsxApi.toObject("report", {
mode: "table",
allSheets: true,
headerRow: 1,
firstDataRow: 2,
skipEmptyRows: true
});
InDriver.debug(JSON.stringify(obj));
XlsxApi.close("report");
Pattern 4 — modify XLSX file and save copy
onStartup:
InDriver.import("XlsxApi");
if (!XlsxApi.open("report", InDriver.currentPath() + "/report.xlsx")) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}
XlsxApi.writeA1("report", "A1", "Processed by InDriver");
XlsxApi.writeA1("report", "B1", new Date().toISOString());
XlsxApi.saveAs("report", InDriver.currentPath() + "/report_processed.xlsx");
XlsxApi.close("report");
When generating code using XlsxApi, follow these rules:
InDriver.import("XlsxApi");
if (!XlsxApi.open("report", file)) {
InDriver.debug(XlsxApi.lastError(), "critical");
return;
}