Apps

The VisionAppster Engine can be thought of as a platform-independent light-weight operating system that runs on top of a real operating system. In the operating system analogy, apps are platform-independent executables.

Apps are written in QML. The structure of the app is built using QML components while the application logic is written in JavaScript. QML makes it easy to create user interfaces to vision apps, but it is also used to build server apps that run in a displayless environment.

Usually, apps are created with the Builder and run by the Engine service. To create an app that can be run by va-run or the Engine service you just need to export the app from the Builder as a .vapkg file.

It is also possible to create apps manually yourself. This document gives an overview of how apps can be built and run.

Main app component

At a bare minimum, an app consists of a single qml file (e.g. app.qml) with the following contents:

import VisionAppster.Core 1.0

App
{
  function start() {}
}

This declares a single QML component of type App. The component must contain a start function, which will be invoked when the app is started. Unlike the main function in many programming languages, the purpose of the start function is not to run until the program ends. Instead, it is an event handler whose purpose is to prepare the app for execution.

You can run this app with va-run:

va-run app.qml

If you installed a sandboxed version:

flatpak run --command=va-run com.visionappster.Builder app.qml

This will cause the Engine to start the app and remain listening to events from various sources, which you haven't defined yet. To stop the Engine and hence the app, you need to press Ctrl+C or kill the process otherwise.

If the app component defines a function called stop, it will be called before the app exits. The stop function can return false to indicate that it cannot be stopped right now. It is however always possible to kill an app that refuses to shut down cleanly. When shut down, the Engine first tries to stop all running apps and then kills them.

import VisionAppster.Core 1.0

App
{
  function start() {}
  function stop() {
    console.log("Stopping");
  }
}

To stop an app, call AppManager.stop(appInfo.pid). To abruply stop execution, one can call die() in the app's context. The function takes an optional error message as a parameter.

Structure of an app

In addition to the main component, an app may contain any number of other components, JavaScript files and resources such as images and text files. You are free to arrange these in any directory structure as far as the root directory contains an app description file called appconfig.json.

The app description file defines the entry point of the app and any other meta-data that may be used by the app itself or other apps. For example, the web management interface uses the appName meta-data entry as the user-visible name of an app. A minimal configuration file could be like this:

{
  "appName":       "My Application",
  "mainComponent": "app.qml",
  "appId":         "my.test.app/1"
}

At run time, the contents of the app configuration file are accessible through a global object called appInfo. The object contains not only the contents of the configuration file but also information collected at start-up:

uri : string
The full URI of the app, either an absolute path with a file:// schema or a HTTP URI. This field will always be set.
pid : integer
The process ID of the app.
args : array<string>
Start-up (e.g. command-line) arguments.
appId : string
The unique ID of the app. If this is not given in the configuration file, one will be automatically generated from the URI of the app.

The following code prints out the appName and pid of the app.

import VisionAppster.Core 1.0

App
{
  function start() {
    console.log(appInfo.appName); // from appconfig.json
    console.log(appInfo.pid);     // generated at start-up
  }
}

On Threading

In the VisionAppster Engine, heavy processing such as image analysis is not done in the main thread. QML and JavaScript are however inherently single-threaded. Therefore, the JavaScript code defining application logic will always be run in a single thread. Furthermore, the design choice has been to execute the application logic JS code of all running apps in the main thread. This makes inter-app communication easier but has the drawback that a poorly behaving app may hang others. If an app runs into an eternal loop, the main thread will be blocked and no events will be delivered to any app.

It should be noted that JS code embedded in a processing graph can be run in parallel threads. This is possible because interactions between other components of the app are limited by the interface of the tool.