Desktop client for the Telecontrol SCADA system: real-time and historical monitoring, alarm handling, and device configuration for industrial and power-system installations. C++23 and Qt 6, on Windows and macOS.
User manual English / Русский · Screen gallery
The operator workbench: object explorer, event journal with severity banding, and the Inspector showing the selected signal's value, quality and setpoints. The client follows the host OS light/dark preference — every screen below is captured in both appearances.
![]() |
![]() |
| Trends — stacked time-series panes with per-series min/max/mean and a shared time cursor. | Displays — single-line schematics driven by live values. |
![]() |
![]() |
| Alarms and events — severity-banded journal, filtered by zone, severity and period, with acknowledgment. | Protocol debugger — every client↔server request traced with phase and duration. |
- Browse the server's object model — devices, signals, and their live values
- Real-time and historical trends, with cursors, limits and CSV export
- Event and alarm journals, with acknowledgment and per-zone filtering
- Single-line displays and free-form tables built on the same data
- Device configuration: parameters, address maps, limits, bulk create
- Users, roles and password policy; an audit log of who changed what
- Per-device diagnostics, metrics, and a decoded protocol frame log
- Printing and print preview; user profiles with saved page layouts
- Russian and English UI
Three are registered through the REGISTER_DATA_SERVICES macro, selected at
login:
| Backend | Protocol | Default address |
|---|---|---|
| Scada | Telecontrol (gRPC) | localhost |
| OPC UA | OPC UA | opc.tcp://localhost:4840 |
| Vidicon | Vidicon | localhost |
The servers those talk to speak IEC 60870-5-104, IEC 61850, Modbus and OPC UA to the field. Modus 6.30 schematics are integrated on Windows through ActiveX.
This repository does not build standalone yet. The client resolves the six products it consumes as sibling checkouts. Five are public:
| Consumed product | What it is | Source |
|---|---|---|
scada-core |
base utilities, gRPC protocol, metrics | public |
scada-common |
address space, node services, OPC UA types | public |
opcuapp |
OPC UA SDK | public |
graph_qt |
the charting widget | public |
view_manager_qt |
dockable view management | public |
display |
the schematic display runtime | proprietary |
The sixth, display, is the Vidicon schematic runtime and stays closed — not a
publication that has not happened yet, but one that is not going to happen.
Instead it will ship as a prebuilt binary behind a C ABI, which the client
loads rather than compiles, so this repository will build from its public
dependencies without the schematic display ever becoming public.
Until that lands the client needs display as source, so treat a clone as
sources to read rather than a build to run — which is also why the CI here is
static analysis. The build instructions below are the real ones, and they work
in a checkout that has all six.
cmake --preset ninja # Configure
cmake --build --preset release # Build (or: debug, relwithdebinfo)
ctest --preset test-release # Test (or: test-debug)
cmake --build --preset relwithdebinfo --target run # Build and launchEvery product in the SCADA tree carries this same preset set, so the commands
do not change from one to the next. Export VCPKG_ROOT before configuring —
the preset names the vcpkg toolchain through it, and a toolchain file is read
before any project CMake runs, so nothing else can supply it. Everything else
machine-specific — ccache, cppcheck, and on Windows the MSVC include and lib
directories — lives in one .scada-local.cmake beside build-support/.
- A C++23 compiler (MSVC, Clang, or GCC)
- CMake 3.25+ with vcpkg
- Qt 6 —
qtbase(widgets),qttools,qttranslations;qtactiveqton Windows - Boost — Asio, Beast, Signals2, Algorithm, Range, JSON, Process, Program Options, Date Time, System
- GoogleTest
- Windows SDK / ATL, for the Windows-only COM and ActiveX modules
Those come from vcpkg.json; the sibling products come from the
resolver in build-support/.
.github/workflows/ci.yml runs static analysis
only, on push and pull request to main and release/**. There is no build
job: a public runner cannot assemble one while display is closed, and it
would also need a multi-hour Qt source build with no binary cache. A build job
returns when the client links a packaged display binary instead of consuming
it as source.
The analyze job runs the same cppcheck configuration the local build runs,
against this repository's own .cppcheck-suppressions. It builds a pinned
cppcheck (2.21.0) from source rather than installing the distro package, which
is eight releases behind and disagrees with it in both directions. error:
findings gate the job; warnings are uploaded as an artifact.
scada-client/
├── app/ # Entry point and ClientApplication (qt/ holds main())
├── aui/ # Abstract UI layer: toolkit-free grid/tree/table models
├── base/ # Foundation utilities (command line, blinker, JSON, files)
├── clipboard/ # Clipboard and node serialization
├── controller/ # MVC controller layer, view management, command registry
├── core/ # Command registries, tracer, progress host
├── main_window/ # Window lifecycle, pages, docks, status bar
├── modules/ # 45 feature modules — see below
├── profile/ # User profiles, window definitions, page layouts
├── properties/ # Property management and dialogs
├── res/ # Resources and settings
├── resources/ # Command ids and icon-strip paths
├── screenshots/ # Generated screen gallery + image_manifest.json
├── services/ # Shared services (speech, tasks, telemetry)
├── test/ # Integration tests, shared fixtures, E2E
├── tools/ # Screenshot generator and source-only checks
├── ui/ # Shared widgets
└── web/ # Web component
modules/ is where the features live — graph, events, table, summary,
sheet, timed_data, portfolio, favorites, filesystem, print,
export, configuration, administration, debugger,
device_diagnostics, device_metrics, bulk_create, limits, login,
settings, inspector, transmission, and the Windows-only modus and
vidicon, among others.
A module with UI keeps its toolkit-specific code in a qt/ subdirectory and
its models in the module root, which is what keeps those models testable
without a QApplication. The client_module() CMake helper creates the
<name>_qt target for each one automatically.
Modular MVC with context-based dependency injection. Each module declares a
*Context struct of its dependencies and privately inherits from it, so what
a module needs is explicit rather than reached through globals:
main() → AppInit → ClientApplication → [CoreModule, EventModule, MainWindowModule, ...]
Async work is coroutine-first (co_await over Boost.Asio), with promise<T>
kept at older module boundaries.
| Switch | Description |
|---|---|
--verbose-logging |
Enable verbose log output |
--log-service-read |
Log service read operations |
--log-service-browse |
Log service browse operations |
--log-service-history |
Log service history operations |
--log-service-event |
Log service events |
--log-service-model-change-event |
Log model change events |
--log-service-node-semantics-change-event |
Log node semantics change events |
The gallery is rendered, not hand-captured: an offscreen Qt
build drives real windows from a JSON fixture, so a UI change lands as a
reviewable image diff. It builds as its own executable,
client_screenshot_generator, in tools/screenshot_generator/.
cmake --build --preset release --target client_screenshot_generator
cd build/ninja/bin/Release
QT_QPA_PLATFORM=offscreen ./client_screenshot_generator --out=path/to/output--out is required, and QT_QPA_PLATFORM=offscreen is not optional on a
headless host. Nothing rebuilds the generator for you, so build it before
reading any change in its output as a regression.
| Option | Meaning |
|---|---|
--out=<dir> |
Output directory (required) |
--image-manifest=<path> |
Path to screenshots/image_manifest.json |
--data=<path> |
Fixture to drive, instead of the source tree's screenshot_data.json |
--only=<names> |
Comma/semicolon/newline-separated filenames to capture |
--theme=<name> |
Render under a design-token theme: dark, light or hc |
Anything but --gtest_* that is not in that table is rejected rather than
ignored. --gtest_filter selects among the ScreenshotGenerator.* captures;
CaptureAllWindows writes one PNG per window type and CaptureMainWindow
writes the composite workbench.
After regenerating, refresh the gallery index:
python3 screenshots/render_gallery.py --renderA bare render_gallery.py is the check that it is current, and runs as the
client_screenshot_gallery_check ctest.
The user manual — operator guides, device configuration, protocol notes — is
published bilingually at
telecontrol-ru.github.io/scada
(English). Most of its UI images
are the captures in screenshots/.
This repository is generated. Development happens in a private monorepo and the public repository is regenerated from it, so a merged pull request here would put this branch ahead of the monorepo and block every later publish — the exporter refuses a non-fast-forward rather than force-pushing over it.
So please open an issue instead; patches are welcome as patches, and reach this repository through the next export. CONTRIBUTING.md has the detail and what to include in a report.
Except for a security issue — this client talks to equipment, so a report that is exploitable should not be public before there is a fix to upgrade to. SECURITY.md says where to send it.
Copyright (C) 2012-2026 Telecontrol, Ltd. (ООО "Телеконтроль").
GPL-3.0 — see LICENSE for the licence text and COPYRIGHT for the notice that covers this work. Third-party asset notices are in NOTICE.
The client draws schematic displays by loading a separate, proprietary shared library at run time — the schematic display runtime — which is not part of this repository and is not free software. Combining a GPL work with a library it is specifically designed to require needs an additional permission under GPL section 7; the draft of that permission is LICENSE-EXCEPTION.txt.
It is a draft and grants nothing yet. Until it is adopted, the client is under the GPL alone, and the runtime is not distributed. The client builds and runs without it: schematic windows report that there is no display runtime, which is a supported state rather than a broken build.




