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 facadecppAppEngine→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:
OpcUaManagerruns on the GUI thread. It owns the models (OpcUaModel,DataAccessModel,AttributesModel,TrendModel) and is the single object QML talks to.OpcUaServiceruns on a dedicated worker thread created byAppEngine. It owns theQOpcUaClientand 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.ServerRuntimeControllerowns aQProcessrunning 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
READYline with its endpoint on stdout and acceptsSTOPon stdin. A separateQLocalSocketdiagnostics 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)
- QML calls a method on
cppManagerOpcUa(OpcUaManager). OpcUaManagerforwards the request toOpcUaServicevia a queued signal.OpcUaServiceperforms the OPC UA call on the worker thread.- The result returns as a queued signal;
OpcUaManagerupdates the relevant model (OpcUaModel/DataAccessModel), which QML is bound to.
Configuration and storage
AppPathsdecides 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). LocaleControllerinstalls and live-switches the UI language.