Base Hemera Concepts

Hemera builds on top of a series of concepts which has to be kept in mind while developing applications for it. Follows a brief explanation of each one of them.

Hemera and Qt

Hemera is built around Qt5 (http://qt-project.org), and Qt5 is as such available on every Hemera installation. Although Qt5 is the preferred way to build Hemera application, it is NOT compulsory to build applications on top of Qt5, for what concerns both logic and UI.

The reference SDK is for C++/Qt5. The SDK has bindings for languages such as Python, where Qt5 bindings are available. Other languages, such as Java, have either native bindings or have no SDK bindings at all. In case a language has no Hemera SDK available, it can be used via a mechanism called proxy application. Proxied applications are slaves to an actual Hemera application, and have limited integration with the underlying Hemera system, albeit being fully functional.

Qt5/C++ is still the recommended choice for application development, especially on devices where performance or other constraints are a priority.

Hemera's system philosophy

For what concerns developers, administrators and integrators, Hemera's philosophy boils down to this:

Make the most basic/common use case extremely simple, provide a customizable way to implement the corner cases.

We try to build the system's experience so that most common use cases are a matter of "pushing a button", while not forgetting about any potential uncommon use case one might have. This is reflected in our default configuration, the API design and more. Hemera is designed to make common/overhead tasks simple, while efficiently assisting you in doing the hard parts.

Sandboxes and Orbits

Hemera stands on the shoulders of Gravity, a middleware which manages in its entirety the execution of the device and its applications. Applications are usually slaves to Gravity, but can also interact with it. This happens through the class Hemera::Gravity.

Gravity works with a system of Orbits. In each device, a Gravity Center (the main middleware) holds together a variety of Orbits. Each Orbit acts as a full-fledged container for applications, capable of downscaling privileges and allowing access to specific resources of the system, called a Sandbox. For simplicity, Orbits and Sandbox are usually used as synonyms, although specifically the Orbit is the container in which the Sandbox is run. Especially when creating and configuring Orbits, the Sandbox is configured as part of the Orbit itself, so this detail is hidden.

For specific documentation on what can be done with Sandboxes, please refer to the api documentation of the Hemera:Qml::Settings namespace, which holds all the configuration objects and their documentation. Specifically, Hemera::Qml::Settings::Sandbox has the various settings which can be tweaked and some tutorials on how to configure Gravity (Hemera::Qml::Settings::Appliance) and create Orbits.

Applications can be launched and live only inside an Orbit. Orbits can be launched and created only by the Gravity Center. An application, as such, has access only to resources available in its Orbit/Sandbox, and is secured against any other potential misuse. It is also logically and physically separated from other applications in other Orbits. IPC for applications in different Orbits is implemented via Hyperspace.

Orbit Handlers and Displays

Gravity Center starts and creates Orbits through a set of intermediaries called Orbit Handlers. Each Orbit handler has one and only one active orbit, which can change at runtime. Switching the active orbit is the most common way of "launching" an application in Hemera.

Each Gravity appliance, by default, has an Orbit Handler tied to the appliance itself. This Orbit Handler is peculiar as it has, in addition to its active orbit, a "Resident Orbit". The resident orbit is the first orbit started in the system and the last one to be shut down, and cannot be changed at runtime. The resident orbit is meant to be a convenient container for user-defined system daemons or equivalent applications which need to run the whole time, and is always headless - GUI applications will refuse to start. Also, if no active or resident orbit is defined, the default Orbit handler won't be activated. Usually, this handler is activated in headless appliances and ignored in GUI appliances.

Additional Orbit Handlers can be instantiated if tied to a display. Each display can have one and only one Orbit Handler tied to it, just like any Orbit Handler which is not the default one needs to have one and only one display tied to it. When configuring Gravity, this detail is hidden behing the Hemera::Qml::Settings::Display object, which implicitly instantiates an Orbit Handler.

With this logic, each display can run independently in the same system, and can be Sandboxed as well. For the most common use (a device with a single display), a sample configuration file looks like this:

import com.ispirata.Hemera.Settings 1.0
GUIAppliance {
name: "SDKDefaultAppliance"
WaylandDisplay {
name: "MainDisplay"
device: "/dev/fb0"
activeOrbit: "comispirataHemeraSDKWelcomeQQ2"
}
}

A Hemera::Qml::Settings::GUIAppliance is instantiated, to notify Gravity that our Appliance is not Headless. The default Orbit Handler is not activated, as its resident and active orbits are undefined, and we instantiate a Display running Wayland on /dev/fb0. This display's Orbit Handler will start with "comispirataHemeraSDKWelcomeQQ2" as its active orbit.

The resource system

Hemera applications can access their resource files through a specific resource system. Differently from Qt5's qrc, those resources represent actual resources in the filesystem in a specific hierarchy which allows an application to either enclose its resources into its Application or Orbit domain, or allow other applications to access them. This is in particular useful for resources such as configuration files, which might be installed with a default version, and afterwards be modified and kept "private".

Resources can be accessed as network resources via the resource:// protocol, which is made available from Hemera's Qt5 SDK.

Note
When loading QML files in a Hemera application, it is possible to access resource:// from within QML.

Stateful applications

Applications in Hemera are state machines driven by their Orbits. An application can be either Stopped or Running, and goes through an initialization and shutdown phase. These states cannot be controlled by the developer in any way, and are instead domain of the Orbit manager, which takes care of controlling the applications depending on Gravity's status.

The developer, instead, must reimplement the various state transition operations to have its application play well with Gravity. In case, though, the developer is using one of the simple UI application templates, this operation is not even necessary, as the state machine is implicitly handled to bring up and down the UI.

Note
As previously mentioned, proxied applications are not stateful.

Orbital Applications

Applications generate by default their own Orbit, which contains only the application itself. Applications launched inside their own Orbit are called "Orbital Applications", and are the most common way to start applications in Hemera, especially when developing but also in deployments. Unless you need to have Orbits with multiple applications, Orbital Applications are also the recommended way to deploy and start applications in Hemera.

Given Hemera::Qml::Settings::Application inherits Hemera::Qml::Settings::Sandbox, Orbital Applications have no configuration limit, and the Sandbox configuration goes straight into the application's manifest file (see The backbone of a Hemera Application).

Orbital applications always generate an Orbit named after your application id without dots. So, the application "com.test.my.app" generates an orbit named "comtestmyapp".

Developer Mode

Any Hemera device can be either in production or development mode. When in development mode, a special Gravity feature named Developer Mode is active. This feature allows to launch applications in simulated orbits for testing and debugging, and in general to alter the device's lifecycle.

With Developer Mode enabled, Hemera SDK's tools can be used to pilot and deploy applications.

Asynchronous object model

Hemera's SDK and internals are completely built around a concept of asynchronicity. This is achieved throughout two main classes: Hemera::AsyncInitObject and Hemera::Operation.

AsyncInitObject
Hemera::AsyncInitObject represent a late initialization object. Upon its creation, the object does not do anything: it instead initializes after an explicit init() is given. When the initialization procedure is completed, the object will be ready to use.

Most of the classes inside Hemera's Qt5 SDKs are direct subclasses of Hemera::AsyncInitObject. In the same fashion, Hemera::AsyncInitObject is meant also to be subclassed and used by the user, in case he has a need of such a feature in his own classes.

Operation
Hemera::Operation is the counterpart to Hemera::AsyncInitObject, representing instead an operation which will be completed in the future. Differently from AsyncInitObject, an Hemera::Operation is instead started upon its creation. Inside Hemera's SDK, every non-trivial procedure is represented as an Operation, to be asynchronous and most of all to allow the developer to monitor the procedure's result and completion.

Just like AsyncInitObject, Hemera::Operation can be subclassed from developers to implement their own procedures. A peculiarity of Hemera::Operation is that by default, no return value is given besides success or failure. A set of specialized subclasses carry with them actual data as results. As a convenience, subclasses should implement a method named result() to expose returned data.

Note
The reason why Hemera::Operation was not implemented through templates, preventing as such the need of subclassing, is its QObject inheritance.