4. Structure and Modules Description

Version 8.3 by Iaroslav Platonov on 2026/06/08 10:08

/* ── Disable XWiki automatic heading counters inside our content ── */
.doc-content h1::before,.doc-content h2::before,
.doc-content h3::before,.doc-content h4::before,
.doc-content h5::before,.doc-content h6::before {
  content:none !important; display:none !important;
}
.doc-content { counter-reset:none !important; }

/* ── Reading progress bar ── */
#rn-prog { position:fixed;top:0;left:0;height:3px;width:0;background:#005AB4;z-index:9999;pointer-events:none; }

/* ── Page content wrapper ── */
.doc-wrap { position:relative; }
.doc-content {
  margin-right:224px;
  border-left:1px solid #E8E8E8;
  border-right:1px solid #E8E8E8;
  padding:0 32px;
}

/* ── Right "On This Page" TOC ── */
.doc-toc {
  position:absolute;top:0;right:0;width:200px;
  font-size:12.5px;line-height:1.55;
  padding:20px 0 20px 16px;
}
.doc-toc.is-fixed { position:fixed;width:200px;overflow-y:auto;z-index:10; }
.toc-header { display:flex;justify-content:space-between;align-items:center;margin-bottom:10px; }
.toc-header strong { font-size:13px;font-weight:700;color:#222; }
.toc-toggle { background:none;border:none;cursor:pointer;color:#555;font-size:14px;padding:0 2px;line-height:1; }
.toc-toggle:hover { color:#222; }
.doc-toc ul { list-style:none;margin:0;padding:0; }
.doc-toc li { margin:0; }
.doc-toc a { display:block;padding:3px 0;color:#555;text-decoration:none;border-left:2px solid transparent;transition:color .12s; }
.doc-toc a:hover { color:#222;text-decoration:underline; }
.doc-toc a.toc-on { color:#222;font-weight:700;border-left:2px solid #005AB4;padding-left:5px; }
.tl1 > a { font-weight:600;color:#222; }
.tl2 > a { padding-left:14px; }
.tl3 > a { padding-left:26px;font-size:12px; }

/* ── Typography ── */
.doc-content h1 { font-size:1.6em;font-weight:700;color:#222;margin:0 0 6px 0;border:none; }
.rn-meta { font-size:.8em;color:#555;margin:0 0 28px 0; }
.doc-content h2 { font-size:1.15em;font-weight:700;color:#222;margin:36px 0 10px 0;display:flex;align-items:baseline;gap:7px;scroll-margin-top:16px; }
.doc-content h3 { font-size:1em;font-weight:700;color:#222;margin:22px 0 8px 0;display:flex;align-items:baseline;gap:6px;scroll-margin-top:16px; }
.doc-content h4 { font-size:.93em;font-weight:700;color:#222;margin:14px 0 6px 0;display:flex;align-items:baseline;gap:5px;scroll-margin-top:16px; }
.doc-content p { font-size:.93em;color:#333;line-height:1.75;margin:0 0 12px 0; }
.doc-content strong { color:#222; }
.doc-content code { background:#f5f5f5;border:1px solid #E8E8E8;border-radius:3px;padding:1px 5px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:.82em;color:#222;line-height:1; }

/* ── Anchor icon ── */
.anchor-icon { color:#E8E8E8;text-decoration:none;opacity:0;transition:opacity .15s;vertical-align:middle;margin-left:4px;display:inline-flex;align-items:center; }
h2:hover .anchor-icon,h3:hover .anchor-icon,h4:hover .anchor-icon { opacity:1; }
.anchor-icon:hover { color:#005AB4; }

/* ── Callout notes ── */
.note { border-radius:6px;padding:12px 16px;margin:14px 0;border:1px solid transparent; }
.note-info    { background:#e1f1f9 !important;border-color:#b3d4ee !important;border-left:4px solid #005AB4 !important; }
.note-warning { background:#fff4ec !important;border-color:#f5cba7 !important;border-left:4px solid #DD5600 !important; }
.note-tip     { background:#e5f3df !important;border-color:#b7dba5 !important;border-left:4px solid #73A839 !important; }
.note-hd { font-weight:700;font-size:.85em;display:flex;align-items:center;gap:5px;margin-bottom:5px; }
.note-info    .note-hd { color:#003d80 !important; }
.note-warning .note-hd { color:#a03d00 !important; }
.note-tip     .note-hd { color:#3d6b1a !important; }
.note-body { font-size:.875em;color:#222 !important;line-height:1.65; }

/* ── Section index list ── */
.section-index { list-style:disc;padding-left:20px;margin:0 0 20px 0; }
.section-index li { margin-bottom:12px; }
.section-index li a { color:#005AB4;font-weight:600;font-size:.93em;text-decoration:underline; }
.section-index li p { margin:3px 0 0 0;font-size:.875em;color:#333;line-height:1.6; }

/* ── Parent topic ── */
.parent-topic { font-size:.82em;color:#555;margin:18px 0 24px 0;border-top:1px solid #E8E8E8;padding-top:10px; }
.parent-topic a { color:#005AB4; }

/* ── Accordion ── */
details.acc { border:1px solid #E8E8E8;border-radius:4px;margin-bottom:5px; }
details.acc summary { display:flex;align-items:center;gap:8px;padding:9px 12px;cursor:pointer;list-style:none;background:none;user-select:none; }
details.acc summary::-webkit-details-marker { display:none; }
details.acc summary::before { content:'▶';font-size:.58em;color:#555;transition:transform .18s;flex-shrink:0; }
details.acc[open] summary::before { transform:rotate(90deg); }
details.acc summary:hover { background:#f5f5f5; }
.acc-num { font-size:.8em;color:#555;min-width:28px;font-weight:600; }
.acc-title { font-size:.9em;font-weight:700;color:#222;flex:1; }
.acc-body { padding:10px 14px 12px 14px;font-size:.88em;color:#333;line-height:1.7; }

/* ── Images ── */
.fig { margin:12px auto 24px auto;width:fit-content;max-width:100%; }
.fig img { border-radius:4px 4px 0 0;border:1px solid #E8E8E8;display:block;cursor:zoom-in;max-width:100%;box-shadow:0 1px 4px rgba(0,0,0,.10);transition:box-shadow .15s; }
.fig img:hover { box-shadow:0 3px 12px rgba(0,90,180,.18); }
.fig-cap { font-size:.78em;color:#555;margin:0;padding:5px 8px;background:#f5f5f5;border:1px solid #E8E8E8;border-top:none;border-radius:0 0 4px 4px;display:block;width:100%;box-sizing:border-box; }

/* ── Lightbox ── */
#rn-lb { display:none;position:fixed;inset:0;z-index:9998;background:rgba(0,0,0,.8);cursor:zoom-out;align-items:center;justify-content:center; }
#rn-lb.open { display:flex; }
#rn-lb-wrap { position:relative;cursor:default;max-width:92vw;max-height:90vh; }
#rn-lb-wrap img { max-width:100%;max-height:90vh;border-radius:5px;display:block; }
#rn-lb-x { position:absolute;top:-13px;right:-13px;background:#005AB4;color:#fff;width:26px;height:26px;border-radius:50%;border:none;cursor:pointer;font-size:15px;display:flex;align-items:center;justify-content:center; }
#rn-lb-cap { position:fixed;bottom:18px;left:50%;transform:translateX(-50%);color:#ddd;font-size:.78em;background:rgba(0,0,0,.55);padding:3px 12px;border-radius:14px;pointer-events:none; }

/* ── Back to top ── */
#rn-top { position:fixed;bottom:60px;right:60px;z-index:9997;width:36px;height:36px;border-radius:50%;background:#005AB4;color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transform:translateY(8px);transition:opacity .22s,transform .22s;pointer-events:none;box-shadow:0 2px 8px rgba(0,90,180,.3); }
#rn-top.on { opacity:1;transform:translateY(0);pointer-events:auto; }
#rn-top:hover { background:#0066cc; }

/* ── Toast ── */
.rn-toast { position:fixed;bottom:66px;right:24px;z-index:9999;background:#222;color:#fff;font-size:.74em;padding:4px 11px;border-radius:4px;pointer-events:none;opacity:0;transition:opacity .18s; }
.rn-toast.on { opacity:1; }

/* ── Tables ── */
.rn-table { width:100%;border-collapse:collapse;margin:8px 0 18px 0;font-size:.875em; }
.rn-table th { background:#f5f5f5;text-align:left;padding:7px 12px;font-size:.78em;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#555;border-bottom:2px solid #E8E8E8; }
.rn-table td { padding:7px 12px;border-bottom:1px solid #E8E8E8;color:#333;vertical-align:top; }
.rn-table tr:last-child td { border-bottom:none; }
.rn-table tr:hover td { background:#f5f5f5; }
.sc { display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;background:#deeaf7;color:#005AB4;border-radius:3px;font-size:.8em;font-weight:700; }

Link copied ✓

1. Introduction

CENTO is a multifunctional software platform for data collection, processing, and display. It can be used to build various automated systems, including dispatching, electricity and energy resource metering, emergency event registration, power quality control, monitoring, and diagnostics.

This document describes the CENTO structure and lists all CENTO components, each with a brief description.

2. CENTO Structure

The general structure of CENTO is shown in Fig. 2.1. CENTO consists of two main blocks:

  • Web part — provides data to users and contains system configuration tools. Information is delivered via a web browser.
  • Core of the system — a set of applications running all CENTO components, responsible for collecting, transmitting, processing, and storing data.
Fig. 2.1 – The general structure of CENTO

Fig. 2.1 – The general structure of CENTO

3. Composition and Description of CENTO Components

3.1. Web Part

The CENTO web interface is built on the nginx web server, web application modules (PHP and executable services), and a web browser on the client side. Key infrastructure components:

  • MongoDB — NoSQL database storing system configuration, event logs, mimic diagrams, and their components.
  • InfluxDB — NoSQL time-series database storing data collected by the system.
  • NATS — message broker for inter-component data exchange.

3.2. The "Core" of the System

The core consists of polling and data transmission services, processing services, and auxiliary services.

Polling and data transmission services collect data from information sources and transmit it to external systems via the message broker. Certain polling services include binary files sed_metrology_XXX.bin — the metrologically significant part containing data extraction mechanisms and register configurations for the polled devices.

Note
When metrologically significant files change, their checksum changes. Checksums are shown on the Services tab of System Information (Settings → System Information) and are calculated using the MD5 algorithm. Click Update Information to refresh.
Fig. 3.1 – Updating checksums of metrologically significant files

Fig. 3.1 – Updating checksums of metrologically significant files

Data processing services receive data from the message broker, process it, and write it to the database. Processing services include metrologically significant files sed_metrology_YYY.bin containing algorithms for calculating measured values. The sed_calc_arch service additionally has sed_metrology_formulas.bin for custom pre-calculation formulas marked as metrologically significant.

Auxiliary services ensure the main CENTO services can perform their functions (license management, watchdog, scheduling, backup, replication, etc.).

The composition of CENTO services for a given installation depends on its configuration and is shown on the System Information tab.

Table 1 — CENTO software components (140 services)

5Data Collection and Transmission Services
ServiceFunction
1sed_bus_dispatcherSequential Device Polling Management Service
2sed_xml_readerService for processing received XML files 80020
3sed_xml_sendXML file transfer service 80020
4sed_archive_webapiWeb API service for transmitting archived data per CENTO protocol
5sed_s2s_configurationConfiguration transmission service between CENTO servers
16Standard Protocol Services
ServiceFunctionMetrologically significant
6sed_modbus_rtdReal-time data from devices using Modbus TCP / RTU over TCPsed_metrology_001.bin
7sed_modbus_rtd_sendSend real-time data to third-party devices via Modbus TCP
8sed_modbus_syncDevice time synchronisation via Modbus TCP / RTU over TCP
9sed_snmp_rtdReal-time data via SNMP (v1, v2c, v3) and ICMP
10sed_iec103_waveformWaveform files from devices via IEC 60870-5-103sed_metrology_393.bin
11sed_iec104_rtdReal-time data via IEC 60870-5-104sed_metrology_011.bin
12sed_iec104_rtd_sendSend real-time data to third-party devices via IEC 60870-5-104sed_metrology_012.bin
13sed_opcda_rtdReal-time data via OPC DA
14sed_opcua_rtdReal-time data via OPC UA
15sed_opcua_archArchived data via OPC UAsed_metrology_402.bin
16sed_opcua_serverData transmission via OPC UA
17sed_dnp3_rtdReal-time data via DNP3
18sed_ftp_waveformWaveform files via FTPsed_metrology_380.bin
19sed_iec61107_rtdReal-time data via IEC 62056-21sed_metrology_61107.bin
20sed_iec61850_archArchived data via IEC 61850
21sed_iec61850_rtdReal-time data via IEC 61850
45Proprietary Protocol Services
ServiceFunctionMetrologically significant
22sed_satec_archArchived data and event logs from SATEC devicessed_metrology_7_8.bin
23sed_satec_pq_journalPower quality event logs from SATEC devicessed_metrology_106.bin
24sed_satec_pq_reportPower quality reports from SATEC devicessed_metrology_106.bin
25sed_satec_waveformsWaveforms from SATEC via Modbus TCP / RTU over TCPsed_metrology_108_109.bin
26sed_iec103_rtdReal-time data from TOR 200 terminals via IEC 60870-5-103sed_metrology_391.bin
27sed_sepam_waveformWaveforms and time sync from Sepam via Modbus RTU over TCPsed_metrology_298_299.bin
28sed_rtu_over_tcp_logikamp_rtdReal-time data via LogicaMP RTU over TCPsed_metrology_121.bin
29sed_rtu_over_tcp_logikamp_archArchived data via LogicaMP RTU over TCPsed_metrology_122.bin
30sed_axis_video_journalAXIS camera recording servicesed_metrology_435.bin
31sed_vzlyotmb_archArchives and event logs from Vzlet TSRV-043/027, IVK-TERsed_metrology_25_133.bin
32sed_micom_waveformsWaveforms from Micom via Modbus RTU over TCPsed_metrology_370.bin
33sed_pulsar_archArchives from Pulsar via Modbussed_metrology_342.bin
34sed_rtu_over_tcp_a1800_archArchived data from A1800 meters via Modbus RTU over TCPsed_metrology_232.bin
35sed_rtu_over_tcp_a1800_rtdReal-time data from A1800 meters via Modbus RTU over TCPsed_metrology_231.bin
36sed_rtu_over_tcp_acron_archArchived data from Acron meters via Modbus RTU over TCPsed_metrology_142.bin
37sed_rtu_over_tcp_acron_rtdReal-time data from Acron meters via Modbus RTU over TCPsed_metrology_141.bin
38sed_rtu_over_tcp_bmrz_waveformWaveforms from BMRZ terminals via Modbus RTU over TCPsed_metrology_110.bin
39sed_rtu_over_tcp_bzp03_waveformWaveforms from BZP-03 terminals via Modbus RTU over TCP
40sed_rtu_over_tcp_kmp_rtdMultitical 601 polling via KMP protocolsed_metrology_191.bin
41sed_rtu_over_tcp_majac_archArchived data via Mayak protocolsed_metrology_152.bin
42sed_rtu_over_tcp_mercury200_rtdReal-time data via Mercury 200 protocolsed_metrology_251.bin
43sed_rtu_over_tcp_mercury_archArchives and event logs from Mercury-230 via Mercury-230 protocolsed_metrology_202.bin
44sed_rtu_over_tcp_mercury_rtdReal-time data via Mercury 230 protocolsed_metrology_201.bin
45sed_rtu_over_tcp_photon_rtdReal-time data via Photon RTU over TCPsed_metrology_221.bin
46sed_rtu_over_tcp_ref542plus_waveformWaveforms and time sync from ABB REF 542plus via Modbus RTU over TCP
47sed_rtu_over_tcp_set4tm_archArchive polling for SET-4TM / PSC meterssed_metrology_112.bin
48sed_rtu_over_tcp_set4tm_rtdReal-time data via SET-4TM RTU over TCPsed_metrology_111.bin
49sed_rtu_over_tcp_tem104_archArchived data via TEM104 protocolsed_metrology_162.bin
50sed_rtu_over_tcp_tem104_rtdReal-time data via TEM104 protocolsed_metrology_161.bin
51sed_rtu_over_tcp_vist_archArchived data via VIS.T protocolsed_metrology_172.bin
52sed_rtu_over_tcp_vkt5_archArchived data from VKT-5 devicessed_metrology_312.bin
53sed_rtu_over_tcp_vkt5_rtdReal-time data from VKT-5 devicessed_metrology_311.bin
54sed_vkt7_archArchived data from VKT-7 devices
55sed_vkt7_rtdReal-time data from VKT-7 devices
56sed_rtu_over_tcp_vtd_archArchived data from VTD devicessed_metrology_182.bin
57sed_rtu_over_tcp_vtd_rtdReal-time data from VTD devicessed_metrology_181.bin
58sed_uvp_archArchive polling for Promavtomatika UVP series devicessed_metrology_352.bin
59sed_ce_archArchived data from Energomera CE series via IEC61107sed_metrology_362.bin
60sed_alpha_archArchived data from Alfa-3 (Alfamera) devicessed_metrology_462_472.bin
61sed_bug01_rtdArchived data from BUG-01 devicessed_metrology_491.bin
62sed_dymetic_archArchived data from Dimetik devicessed_metrology_552.bin
63sed_irvis_archArchived data via Irvis protocolsed_metrology_442.bin
64sed_karat_archArchived data from Karat devicessed_metrology_512.bin
65sed_lis200_archArchived data from EK-260 / EK-270 (Elster)sed_metrology_452.bin
66sed_rtu_over_tcp_sepam_waveformWaveforms from Sepam via Modbus RTU over TCPsed_metrology_298.bin
3SQL Database Services
ServiceFunctionMetrologically significant
67sed_sql_rtdTelemetry data from MySQLsed_metrology_211.bin
68sed_sql_archArchived data from Microsoft SQL Server
69sed_sql_arch_sendRecording archived data in an external SQL database
8Data Processing Services
ServiceFunctionMetrologically significant
70sed_controllerTelemetry reception and processing → InfluxDBsed_metrology_controller.bin
71sed_events_calcEvent generation based on configured conditions
72sed_calc_archMetering channel calculationsed_metrology_formulas.bin
73sed_calc_rtdPre-calculated tag calculation
74sed_round_archMetering channel calculation with roundingsed_metrology_round_arch.bin
75sed_energy_calcServer-level energy resource calculationsed_metrology_411_421.bin
76sed_loadflow_calcMode calculation based on information model
77sed_storage_writerArchived data recording (metering and event logs) to InfluxDB and MongoDB
29Web Part Services
ServiceFunction
78php5-cgiWeb query service
79nginxWeb server
80sed_uiWeb resources processing service (js, css, html)
81sed_rtd_analysisReal-time data analysis
82sed_authUser authentication, authorisation, and access rights configuration
83sed_auth_privilegesUser privilege management
84sed_api_gatewayWeb request proxy to internal services
85sed_dataset_configuratorDataset management
86sed_rtdReal-time data subscription service
87sed_rtd_archiveArchived data generation from real-time data
88sed_systemSystem information user interface
89sed_archiveElectricity and energy accounting analysis interfaces
90sed_archive_integrityData completeness analysis interfaces
91sed_pq_reportsPower quality reports
92sed_pq_journalPower quality logs
93sed_ras_configuratorEmergency event logger configuration
94sed_reportsReport generation, management, and user interfaces
95sed_widgetsDrag-and-drop widget user interfaces
96sed_menuCENTO system menu display and configuration
97sed_mnemonic_diagramsMimic diagram user interfaces
98sed_configurationSystem configuration interfaces (alerts, mail, formulas, device trees)
99sed_devices_configuratorDevice, tag, and channel configuration editing
100sed_common_event_journalConfigurable event log interfaces
101sed_device_event_journalDevice log service
102sed_operative_journalReal-time event log interface
103sed_video_journalVideo camera log service
104sed_markingDispatch tag management
105sed_soundsSound alert management interface
106sed_video_streamVideo streaming from cameras registered in the information model
28System Services
ServiceFunction
107natsMessage broker and service registry (service discovery)
108influxdbTime series database
109mongodConfiguration and structured data database (event logs, requests)
110sed_signalsInternal signal service (remote control, config update, manual polling, recalculation)
111sed_debugDebugging information access service
112sed_graphqlDevice status subscription service
113sed_energy_calc_configurationEnergy resource calculation configuration management
114sed_backupData backup service (cron-based)
115sed_channelsChannel subscription service
116sed_channels_dataProtocol status and latest channel data subscription service
117sed_emission_archiveEmissions metering data service
118sed_profile_archiveMetering data from InfluxDB via gRPC
119sed_web_storageArchived metering data access service
120sed_watchdogCENTO services launch management
121sed_schedulerScheduled task launcher
122sed_licenseLicensing service
123sed_monitorSystem services and application monitoring
124sed_telecontrolRemote control service
125sed_notificatorUser event notification by e-mail
126sed_waveforms_journalWaveform log service
127sed_storage_configuratorInfluxDB database configuration
128sed_system_configurationCENTO system configuration editing
129sed_user_settingsUser settings configuration
130sed_system_event_journalSystem event registration and provision (business logic and administration)
131sed_tech_event_journalTechnological event processing and management
132sed_information_modelSystem information model management
133sed_events_configuratorSystem event configuration
134sed_information_model_editorInformation model editing
6Server Backup Services
ServiceFunction
135sed_coordinatorCluster host server selection via Bully leader election algorithm
136sed_reserveBackup server list display
137sed_replicatorLog replication between cluster servers
138sed_influx_replicatorData replication (archives and telemetry) between cluster servers
139sed_events_replicatorEvent replication between cluster servers
140sed_fs_synchFile replication (gob files, reports, waveforms) between cluster servers

(function(){
  var bar = document.getElementById('rn-prog');
  var top = document.getElementById('rn-top');
  window.addEventListener('scroll', function(){
    var d = document.documentElement;
    var pct = d.scrollTop / (d.scrollHeight - d.clientHeight);
    bar.style.width = (pct*100)+'%';
    d.scrollTop > 280 ? top.classList.add('on') : top.classList.remove('on');
  }, {passive:true});

  /* TOC active via IntersectionObserver */
  var links = Array.from(document.querySelectorAll('[data-toc]'));
  var secs = links.map(function(a){
    return {el: document.getElementById(a.getAttribute('href').slice(1)), a:a};
  }).filter(function(s){return s.el;});

  var busy = false;
  var obs = new IntersectionObserver(function(entries){
    if (busy) return;
    entries.forEach(function(e){
      if (!e.isIntersecting) return;
      var i = secs.findIndex(function(s){return s.el===e.target;});
      if (i<0) return;
      secs.forEach(function(s){s.a.classList.remove('toc-on');});
      secs[i].a.classList.add('toc-on');
      var toc = document.querySelector('.doc-toc');
      var link = secs[i].a;
      toc.scrollTop = link.offsetTop - toc.offsetTop - toc.clientHeight/2 + link.clientHeight/2;
    });
  }, {rootMargin:'-8% 0px -60% 0px', threshold:0});
  secs.forEach(function(s){obs.observe(s.el);});

  links.forEach(function(a){
    a.addEventListener('click', function(e){
      var id = a.getAttribute('href').slice(1);
      var el = document.getElementById(id);
      if (!el) return;
      e.preventDefault();
      busy = true;
      secs.forEach(function(s){s.a.classList.remove('toc-on');});
      a.classList.add('toc-on');
      el.scrollIntoView({behavior:'smooth',block:'start'});
      setTimeout(function(){busy=false;}, 900);
    });
  });

  /* TOC collapse */
  var tocList = document.getElementById('toc-list');
  var togBtn  = document.getElementById('toc-tog');
  var collapsed = false;
  togBtn && togBtn.addEventListener('click', function(){
    collapsed = !collapsed;
    tocList.style.display = collapsed ? 'none' : '';
    togBtn.textContent = collapsed ? '»' : '«';
  });

  /* Lightbox */
  var lb = document.getElementById('rn-lb');
  var lbI = document.getElementById('rn-lb-img');
  var lbC = document.getElementById('rn-lb-cap');
  window.openLightbox = function(img){
    lbI.src = img.src; lbI.alt = img.alt; lbC.textContent = img.alt;
    lb.classList.add('open'); document.body.style.overflow='hidden';
  };
  window.closeLightbox = function(){
    lb.classList.remove('open'); document.body.style.overflow='';
  };
  lb.addEventListener('click', function(e){if(e.target===lb)closeLightbox();});
  document.addEventListener('keydown', function(e){if(e.key==='Escape')closeLightbox();});

  /* Copy anchor */
  var toast = document.getElementById('rn-toast');
  var t;
  window.copyAnchor = function(id){
    var url = location.href.split('#')[0]+'#'+id;
    if(navigator.clipboard) navigator.clipboard.writeText(url).catch(function(){});
    else { var x=document.createElement('textarea');x.value=url;document.body.appendChild(x);x.select();document.execCommand('copy');document.body.removeChild(x); }
    toast.classList.add('on');
    clearTimeout(t);
    t = setTimeout(function(){toast.classList.remove('on');}, 1600);
  };

  /* ── Sticky TOC via JS ──
     Uses position:absolute→fixed switch. No placeholder needed because
     .doc-content has a fixed margin-right — content width never changes. */
  (function(){
    var toc  = document.querySelector('.doc-toc');
    var wrap = document.querySelector('.doc-wrap');
    if (!toc || !wrap) return;

    var navbar  = document.querySelector('.navbar, nav.navbar, #topBar, header');
    var navH    = navbar ? navbar.offsetHeight : 0;
    var footer  = document.querySelector('footer, .xwikifooter, #xwikifooter, .bottombar, .sticky-container');

    function update() {
      var wr = wrap.getBoundingClientRect();

      if (wr.top <= navH) {
        /* Doc-wrap has scrolled above navbar — fix the TOC */
        var footerTop = footer ? footer.getBoundingClientRect().top : window.innerHeight;
        var h = Math.max(0, Math.min(window.innerHeight, footerTop) - navH);

        toc.classList.add('is-fixed');
        toc.style.top    = navH + 'px';
        toc.style.left   = (wr.right - 200) + 'px'; /* right edge of wrap minus TOC width */
        toc.style.height = h + 'px';
      } else {
        /* Still in natural position */
        toc.classList.remove('is-fixed');
        toc.style.top = toc.style.left = toc.style.height = '';
      }
    }

    requestAnimationFrame(function(){ requestAnimationFrame(function(){
      update();
      window.addEventListener('scroll', update, {passive:true});
      window.addEventListener('resize', update, {passive:true});
    }); });
  })();

  /* ── Left panel navigation hints ── */
  (function(){
    var hints = {
      'Preface':                          'Introduction to CENTO Software documentation',
      '1. User Manual':                   'Web interface guide — login, diagrams, analysis, logs, reports',
      '2. Setup Manual':                  'System configuration and setup for administrators',
      '3. Metering Channels':             'Metering channel configuration and data collection setup',
      '4. Structure and Modules Description': 'CENTO system architecture and module overview',
      '5. Diagrams Development Manual':   'Creating and configuring SCADA mimic diagrams',
      '6. Flexible Reports Guide':        'Building configurable tabular reports from metering data',
      '7. Events Setup':                  'Configuring event rules, thresholds, and notifications',
      '8. Control Setup Manual':          'Setting up remote control and switching operations',
      'Network Diagram (CLOUD)':          'CENTO deployment topology for cloud installations',
      'Network Diagram (On-Prem)':        'CENTO deployment topology for on-premises installations',
      'Server Configuration Example':     'Reference server setup and configuration example',
      'Technical Requirements':           'Hardware and software requirements for CENTO deployment',
      'DEMO server':                      'Access the live CENTO demo environment',
      'Release Notes':                    'Changelog and version history for the CENTO platform',
    };
    document.querySelectorAll('#leftPanels a').forEach(function(a){
      var text = a.textContent.trim();
      var hint = hints[text] || hints[text.replace(/^\d+\.\s*/, '')];
      if (hint) a.setAttribute('data-hint', hint);
    });
  })();
})();