Architecture - SEB103/QtOpcUaManager GitHub Wiki

English · Deutsch · Français · Italiano · Русский · Українська

Architecture

This chapter gives a new developer a fast mental model of the program: the major modules, how the UI reaches the backend, the threading and process boundaries, and how data and commands flow. Each named class links to its page in the OpcUaManager API Reference.

Build targets

The application is built from three static libraries, two executables, and two QML modules (see Project Structure):

  • OpcUaCore — the OPC UA client core, item models, persistence, and the client project layer. Links Qt Core, Network, OpcUa, and Sql.
  • ServerProjectCore — the server-project domain model (.uaserver), serializer, validator, and NodeSet2 I/O. Links Qt Core only so the headless runtime can reuse it without the client stack.
  • OpcUaQmlApi — the QML-facing facades. Depends on OpcUaCore and ServerProjectCore.
  • appOpcUaManager — the GUI executable (app/).
  • OpcUaServerRuntime — the headless open62541 server executable (runtime/).
  • QML modules OpcUaManager (screens) and Base (reusable controls).

The QML-to-C++ boundary

The C++ backend is exposed to QML through context properties, not through registered QML types. The application engine sets these on the root context:

  • cppManagerOpcUa → OpcUaManager (client operations and models)
  • cppServerStudio → ServerStudio (server design and runtime control)
  • cppProjectManager → the client-project facade
  • cppAppEngine → AppEngine (application services)
  • cppAppInfo → AppInfo, cppLocale → LocaleController, cppLicenseModel → LicenseModel, cppUpdate → UpdateController

Because context properties need no import, the reusable Base components stay free of any C++ import; only the application layer binds them to the facades. There are deliberately no Cpp.* QML modules in this code base.

OPC UA client: GUI thread versus worker thread

The client is split across two threads to keep the UI responsive:

  • OpcUaManager runs on the GUI thread. It owns the models (OpcUaModel, DataAccessModel, AttributesModel, TrendModel) and is the single object QML talks to.
  • OpcUaService runs on a dedicated worker thread created by AppEngine. It owns the QOpcUaClient and performs discovery, connect/disconnect, browsing, reads, writes, and subscriptions.

The two communicate through queued signals, so the network- and PKI-facing work never blocks rendering. Decoding of structured (CODESYS-style) values is handled by a helper that browses the instance subtree.

OPC UA server: two processes

The designed server runs as a separate process, not inside the GUI:

  • ServerStudio (GUI) owns the in-memory server project and the editing models, and drives the runtime.
  • ServerRuntimeController owns a QProcess running OpcUaServerRuntime. The runtime embeds open62541, builds the address space from the project, applies security, simulates values, and runs behavior rules.
  • The two communicate over a simple contract: the runtime prints a READY line with its endpoint on stdout and accepts STOP on stdin. A separate QLocalSocket diagnostics side-channel streams live session/uptime JSON.

Open in Client connects the built-in client to the running local server by handing its discovery URL to OpcUaManager.

Data and command flow (client read)

  1. QML calls a method on cppManagerOpcUa (OpcUaManager).
  2. OpcUaManager forwards the request to OpcUaService via a queued signal.
  3. OpcUaService performs the OPC UA call on the worker thread.
  4. The result returns as a queued signal; OpcUaManager updates the relevant model (OpcUaModel / DataAccessModel), which QML is bound to.

Configuration and storage

  • AppPaths decides installed-versus-portable locations and seeds writable storage on first run.
  • Settings live in an INI store; client projects (.uaproj) and server projects (.uaserver) are JSON, written atomically by their serializers.
  • Monitored nodes are persisted in a SQLite database (NodeDatabase).
  • LocaleController installs and live-switches the UI language.