História revízií (prvá verzia, zverejnená 1. 11. 2026)
- Prvé zverejnenie
Citovať tento článok(DOI: 10.5281/zenodo.23968438)
Tento článok je archivovaný v službe Zenodo. Nižšie nájdete DOI, ktoré vždy odkazuje na najnovšiu verziu, aj DOI viazané na verziu, ktorú práve čítate.
Go Komura (2026). Úvod do OPC UA ── Zber údajov z PLC a zariadení v aplikácii pre Windows. KomuraSoft LLC. https://doi.org/10.5281/zenodo.23968438 https://comcomponent.com/sk/blog/opc-ua-windows-data-collection/
- DOI (najnovšia verzia)
- 10.5281/zenodo.23968438
- DOI (táto verzia)
- 10.5281/zenodo.23968439
Keď zbierate údaje z PLC alebo zariadení do aplikácie pre Windows a zariadenie má server OPC UA, niekedy si ušetríte písanie komunikácie zvlášť pre každého výrobcu. Pripojiť sa a čítať hodnoty však nestačí. Ak nepoznáte stav a časové pečiatky, ktoré prichádzajú spolu s hodnotou, odbery, ktoré oznamujú zmeny, povoľovanie pripojenia certifikátmi a spôsob zotavenia po odpojení, nebudete môcť zozbieraným údajom neskôr dôverovať.
Tento článok vysvetľuje tú časť OPC UA, ktorá je potrebná na zber údajov, spolu s výsledkami skutočného spustenia klienta v C#. Ako klienta som použil knižnicu .NET od OPC Foundation a na druhej strane simulovaný server zariadenia. S konkrétnymi PLC ani zariadeniami som to neskúšal. Nastavenia jednotlivých zariadení si overte v ich dokumentácii.
Zber obrázkov z kamier GigE Vision a USB3 Vision je opísaný v článku „Dropped Frames with GigE Vision and USB3 Vision Cameras“ a zariadenia so sériovou komunikáciou v článku „Serial Communication App Pitfalls“.
Rozdiel medzi OPC UA a OPC Classic
Špecifikácie OPC Classic si vymieňajú údaje medzi softvérovými komponentmi cez COM/DCOM vo Windows. OPC DA pre procesné údaje, OPC A&E pre alarmy a OPC HDA pre históriu sú definované ako samostatné špecifikácie1.
OPC Foundation zverejnila OPC UA (Unified Architecture) v roku 2008. OPC UA je servisne orientovaná architektúra, ktorá nezávisí od konkrétnej platformy a spája funkcie jednotlivých špecifikácií Classic. Ako dôvody nového návrhu OPC Foundation uvádza okrem iného to, že Microsoft začal klásť väčší dôraz na multiplatformové mechanizmy než na COM/DCOM, že bolo potrebné bežať aj na systémoch mimo Microsoftu a na vstavaných zariadeniach a že bolo potrebné bezpečne sa pripájať cez brány firewall1.
Špecifikácia OPC UA je normalizovaná aj v IEC ako séria IEC 625412. Pre komunikačný port je v IANA zaregistrovaný 4840/TCP ako opcua-tcp3.
flowchart TB
accTitle: Rozdiel medzi OPC Classic a OPC UA
accDescr: Diagram, ktorý ukazuje, že OPC Classic stojí na COM a DCOM vo Windows a delí sa na samostatné špecifikácie DA, A&E a HDA, zatiaľ čo OPC UA spája ich funkcie do jednej špecifikácie nezávislej od platformy.
C["OPC Classic"] --> D["Beží na COM/DCOM (Windows)"]
D --> DA["Špecifikácie rozdelené na DA, A&E a HDA"]
DA -->|"Spojenie funkcií"| U["OPC UA (2008)"]
U --> P["Nezávisí od konkrétnej platformy"]
P --> S["Normalizované ako IEC 62541"]
Obrázok 1: OPC Classic tvoria samostatné špecifikácie nad COM/DCOM a OPC UA ich spája do jednej.
Ak má existujúce zariadenie len server OPC DA, potrebujete na strane klienta mechanizmus, ktorý používa COM/DCOM. Tento článok sa zaoberá len OPC UA.
V diagrame plná čiara označuje vzťah, ktorý platí vždy, a prerušovaná čiara vzťah podmienený (podmienky sú uvedené pri každom vzťahu na stránke s podrobnosťami). Úplný zoznam vzťahov (spolu 13, s dôkazmi a mierou istoty) a definície hlavných pojmov sú zhromaždené na stránke s podrobnosťami znalostnej mapy (v japončine). Údaje: JSON-LD / Turtle
Vyskúšaná zostava
Spúšťal som túto zostavu.
| Úloha | Použité |
|---|---|
| Klient | C#, .NET 10 SDK 10.0.114, balík NuGet OPCFoundation.NetStandard.Opc.Ua.Client 2.0.1 |
| Server | Simulované zariadenie vytvorené v Pythone 3.13 a asyncua 2.1.0 (port 4840 na tom istom PC) |
| OS | Linux (spustenie vo Windows som neskúšal) |
Knižnica klienta je implementácia .NET, ktorú zverejňuje OPC Foundation. Pri používaní verzie 2.0 som sa riadil postupom z dokumentu Getting started v tom istom repozitári4.
flowchart TB
accTitle: Vyskúšaná zostava
accDescr: Diagram zostavy, v ktorej sa klient v C# pomocou knižnice .NET od OPC Foundation pripája šifrovaným spojením k simulovanému serveru zariadenia v asyncua, ktorý počúva na porte 4840.
A["Klient v C# (LineCollector)"] -->|"opc.tcp, SignAndEncrypt"| B["Simulovaný server zariadenia (asyncua)"]
A -.-> L["Knižnica .NET od OPC Foundation 2.0.1"]
B -.-> V["Štyri premenné objektu Line1"]
Obrázok 2: V rámci jedného PC som sa z klienta v C# pripojil k simulovanému serveru zariadenia.
Simulovaný server zariadenia
Namiesto zariadenia som v asyncua vytvoril malý server5. Pod objektom Line1 sú štyri premenné: teplota (Temperature), počet vyrobených kusov (PartCount), chod (Running) a nastavená hodnota (Setpoint). Teplota sa mení každých 0,5 sekundy, počet kusov každé 2 sekundy. Pripravil som dve úrovne zabezpečenia: bez šifrovania (None) a s podpisom a šifrovaním (SignAndEncrypt s politikou Basic256Sha256).
# Simulované zariadenie na overenie funkčnosti. Nenahrádza skutočné zariadenie ani PLC.
import asyncio, math, random, socket
from pathlib import Path
from cryptography.x509.oid import ExtendedKeyUsageOID
from asyncua import Server, ua
from asyncua.crypto.cert_gen import setup_self_signed_certificate
HERE = Path(__file__).parent
APP_URI = "urn:komurasoft:demo-plc"
async def main():
key, cert = HERE / "server_key.pem", HERE / "server_cert.der"
await setup_self_signed_certificate(
key, cert, APP_URI, socket.gethostname(),
[ExtendedKeyUsageOID.SERVER_AUTH], {"commonName": "DemoPlcServer"})
server = Server()
await server.init()
server.set_endpoint("opc.tcp://0.0.0.0:4840/demo/")
server.set_server_name("DemoPlcServer")
await server.set_application_uri(APP_URI)
server.set_security_policy([
ua.SecurityPolicyType.NoSecurity,
ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt,
])
await server.load_certificate(str(cert))
await server.load_private_key(str(key))
ns = await server.register_namespace("urn:komurasoft:demo-plc:line1")
line1 = await server.nodes.objects.add_object(ua.NodeId("Line1", ns), "Line1")
temperature = await line1.add_variable(ua.NodeId("Line1.Temperature", ns), "Temperature", 25.0)
part_count = await line1.add_variable(
ua.NodeId("Line1.PartCount", ns), "PartCount", ua.Variant(0, ua.VariantType.UInt32))
await line1.add_variable(ua.NodeId("Line1.Running", ns), "Running", True)
setpoint = await line1.add_variable(ua.NodeId("Line1.Setpoint", ns), "Setpoint", 30.0)
await setpoint.set_writable()
async with server:
print("listening", flush=True)
t, count = 0, 0
while True:
await asyncio.sleep(0.5)
t += 1
value = 25.0 + 2.0 * math.sin(t / 4) + random.uniform(-0.05, 0.05)
await temperature.write_value(round(value, 2))
if t % 4 == 0:
count += 1
await part_count.write_value(ua.Variant(count, ua.VariantType.UInt32))
asyncio.run(main())
Tento server prijíma certifikáty pripájajúcich sa klientov bez kontroly. Je to nastavenie na testovanie a od správania skutočného zariadenia sa líši.
Klient v C#
Do projektového súboru som pridal tieto dva balíky.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
</ItemGroup>
</Project>
Klient postupne vykoná pripojenie, prehliadanie (Browse), čítanie, zápis a odber. Význam jednotlivých častí vysvetľujem postupne v ďalších častiach.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Client.Subscriptions;
using Opc.Ua.Client.Subscriptions.Streaming;
using MonitoredItemOptions = Opc.Ua.Client.Subscriptions.MonitoredItems.MonitoredItemOptions;
string url = args.Length > 0 ? args[0] : "opc.tcp://localhost:4840/demo/";
int notifications = args.Length > 1 ? int.Parse(args[1]) : 6;
const string LineNamespace = "urn:komurasoft:demo-plc:line1";
HostApplicationBuilder builder = Host.CreateApplicationBuilder();
builder.Logging.SetMinimumLevel(LogLevel.Warning);
builder.Services
.AddOpcUa()
.AddClient(options =>
{
options.ApplicationName = "LineCollector";
options.ApplicationUri = "urn:localhost:LineCollector";
options.ProductUri = "urn:komurasoft:LineCollector";
options.PkiRoot = Path.Combine(AppContext.BaseDirectory, "pki");
})
.AddDiscoveryAndConnect(options =>
{
options.DiscoveryUrl = url;
options.SecurityMode = MessageSecurityMode.SignAndEncrypt;
options.SecurityPolicyUri = SecurityPolicies.Basic256Sha256;
});
using IHost host = builder.Build();
await host.StartAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(2));
CancellationToken ct = timeout.Token;
try
{
var connect = host.Services
.GetRequiredService<Func<CancellationToken, Task<ManagedSession>>>();
ManagedSession session = await connect(ct);
await using (session)
{
Console.WriteLine($"接続: {session.ConfiguredEndpoint?.EndpointUrl}");
// Index menného priestoru určuje server. Zistíme ho z URI a z neho vytvoríme NodeId
ushort ns = (ushort)session.NamespaceUris.GetIndex(LineNamespace);
var line1 = new NodeId("Line1", ns);
var temperature = new NodeId("Line1.Temperature", ns);
var partCount = new NodeId("Line1.PartCount", ns);
var setpoint = new NodeId("Line1.Setpoint", ns);
// Prehliadanie: vypíše premenné pod Line1
var browser = new Browser(session)
{
BrowseDirection = BrowseDirection.Forward,
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
IncludeSubtypes = true,
NodeClassMask = (uint)NodeClass.Variable,
};
Console.WriteLine("Line1 の変数:");
foreach (ReferenceDescription child in await browser.BrowseAsync(line1, ct))
{
Console.WriteLine($" {child.DisplayName} {child.NodeId}");
}
// Čítanie: okrem hodnoty sledujeme aj stav a dve časové pečiatky
DataValue t = await session.ReadValueAsync(temperature, ct);
Console.WriteLine($"Temperature = {t.WrappedValue} 状態 {t.StatusCode}");
Console.WriteLine($" Source {Hms(t.SourceTimestamp)} / Server {Hms(t.ServerTimestamp)}");
// Pri čítaní neexistujúceho uzla sa namiesto hodnoty vráti stav Bad
try
{
await session.ReadValueAsync(new NodeId("Line1.Pressure", ns), ct);
}
catch (ServiceResultException ex)
{
Console.WriteLine($"Line1.Pressure の読み取り: {ex.StatusCode}");
}
// Zápis: nastaví Setpoint na 32.5
var write = new WriteValue
{
NodeId = setpoint,
AttributeId = Attributes.Value,
Value = new DataValue(new Variant(32.5)),
};
WriteResponse wr = await session.WriteAsync(null, new[] { write }, ct);
Console.WriteLine($"Setpoint の書き込み: {wr.Results[0]}");
// Odber: požiadame server o vzorkovanie každých 500 ms a o oznamovanie zmien
Console.WriteLine($"購読({notifications}件まで):");
await foreach (DataValueChange change in session.DefaultStreaming
.SubscribeDataChangesAsync(new[] { temperature, partCount }, options: new MonitoredItemOptions
{
SamplingInterval = TimeSpan.FromMilliseconds(500),
QueueSize = 5,
}, ct: ct)
.TakeAsync(notifications)
.WithCancellation(ct))
{
DataValue v = change.Value;
string name = change.MonitoredItem?.Name.Split("s=")[^1] ?? "?";
Console.WriteLine($" {Hms(v.SourceTimestamp)} {name,-17} {v.WrappedValue,6} {v.StatusCode}");
}
}
}
catch (ServiceResultException ex)
{
Console.Error.WriteLine($"OPC UA エラー {ex.StatusCode}: {ex.Message}");
Environment.ExitCode = 1;
}
finally
{
await host.StopAsync();
}
static string Hms(DateTimeUtc time) => time.ToDateTime().ToString("HH:mm:ss.fff");
Pri prvom spustení sa pripojiť nepodarilo. Dôvod vysvetľujem v časti „Povolenie pripojenia certifikátmi“. Po tom, čo som certifikátu servera začal dôverovať, bol výstup takýto. Varovné záznamy knižnice som vynechal.
接続: opc.tcp://localhost:4840/demo/
Line1 の変数:
Temperature ns=2;s=Line1.Temperature
PartCount ns=2;s=Line1.PartCount
Running ns=2;s=Line1.Running
Setpoint ns=2;s=Line1.Setpoint
Temperature = 23.09 状態 Good [0x00000000]
Source 13:40:51.302 / Server 13:40:51.302
Line1.Pressure の読み取り: BadNodeIdUnknown [0x80340000]
Setpoint の書き込み: Good [0x00000000]
購読(6件まで):
13:40:51.302 Line1.Temperature 23.09 Good [0x00000000]
13:40:50.801 Line1.PartCount 67 Good [0x00000000]
13:40:51.802 Line1.Temperature 23.05 Good [0x00000000]
13:40:52.304 Line1.Temperature 23.07 Good [0x00000000]
13:40:52.806 Line1.Temperature 23.2 Good [0x00000000]
13:40:52.806 Line1.PartCount 68 Good [0x00000000]
Časy sú v UTC.
Údaje zo zariadenia sa zobrazujú ako uzly adresného priestoru
Server OPC UA zverejňuje údaje, ktoré má, ako súbor uzlov. Tento súbor sa nazýva adresný priestor. Uzly sú rôznych druhov, napríklad objekty predstavujúce priečinky alebo zariadenia a premenné s hodnotami, a navzájom ich spájajú odkazy. Klient prechádza odkazy službou Browse a zisťuje, aké uzly existujú6.
Vo výstupe vyššie klient prešiel od Line1 po hierarchických odkazoch a našiel štyri premenné.
flowchart TB
accTitle: Adresný priestor simulovaného zariadenia
accDescr: Diagram, ktorý ukazuje, že pod priečinkom Objects je objekt Line1 a pod ním štyri premenné Temperature, PartCount, Running a Setpoint.
O["Objects"] --> L["Line1 (objekt)"]
L --> T["Temperature (premenná)"]
L --> P["PartCount (premenná)"]
L --> R["Running (premenná)"]
L --> S["Setpoint (premenná, zapisovateľná)"]
Obrázok 3: Adresný priestor simulovaného zariadenia. Klient nachádza uzly tak, že službou Browse prechádza odkazy.
NodeId tvorí menný priestor a identifikátor
Uzly sa identifikujú pomocou NodeId. ns=2;s=Line1.Temperature vo výstupe znamená, že index menného priestoru je 2 a identifikátor je reťazec Line1.Temperature.
Menný priestor je v skutočnosti URI. Špecifikácia určuje, že URI menného priestoru sa v rámci služieb vyjadruje číslom a toto číslo zodpovedá pozícii v poli NamespaceArray servera. Index 0 je menný priestor OPC UA http://opcfoundation.org/UA/7.
Preto číslo indexu nepíšte priamo do kódu, ale zisťujte ho z URI. Kód vyššie zisťuje hodnotu 2 z URI cez session.NamespaceUris.GetIndex(LineNamespace) a z nej vytvára NodeId.
flowchart TB
accTitle: Vytvorenie NodeId z URI menného priestoru
accDescr: Diagram, ktorý ukazuje, že klient vyhľadá URI menného priestoru v poli NamespaceArray servera, získa index a spojením tohto indexu s identifikátorom vytvorí NodeId.
U["URI menného priestoru"] --> A["NamespaceArray servera"]
A --> I["Index (v tomto príklade 2)"]
I --> N["NodeId ns=2;s=Line1.Temperature"]
K["Identifikátor Line1.Temperature"] --> N
Obrázok 4: Index menného priestoru zistite z URI a spojte ho s identifikátorom do NodeId.
Čítanie a zápis vracajú spolu s hodnotou aj stav a časové pečiatky
Hodnoty premenných sa čítajú a zapisujú službami Read a Write8. Čítanie nevracia len hodnotu. Vracia štruktúru DataValue, ktorá obsahuje hodnotu, stavový kód, zdrojovú časovú pečiatku a časovú pečiatku servera9.
| Položka | Význam |
|---|---|
| Hodnota | Hodnota premennej. Keď stavový kód signalizuje chybu, hodnota sa ignoruje a server ju nastaví na null |
| Stavový kód (StatusCode) | Či server dokázal hodnotu získať a poskytnúť |
| SourceTimestamp | Čas, ktorý hodnote priradil zdroj údajov |
| ServerTimestamp | Čas, keď server hodnotu prijal alebo zistil, že je správna |
Horné dva bity stavového kódu znamenajú Good (úspech), Uncertain (čiastočný úspech, na niektoré účely nepoužiteľné) alebo Bad (zlyhanie)10. Good [0x00000000] vo výstupe je Good.
Pri čítaní neexistujúceho Line1.Pressure sa namiesto hodnoty vrátilo BadNodeIdUnknown [0x80340000]. Metóda ReadValueAsync tejto knižnice pri stave Bad vyhodí výnimku4. Ak mechanizmus, ktorý hodnoty zaznamenáva, ukladá vedľa hodnoty aj stavový kód, neskôr rozlíšite hodnoty zaznamenané počas stavu Bad.
flowchart TB
accTitle: Obsah DataValue
accDescr: Diagram, ktorý ukazuje, že DataValue vrátená pri čítaní alebo oznámení obsahuje štyri položky, hodnotu, stavový kód, zdrojovú časovú pečiatku a časovú pečiatku servera, a že pri stavovom kóde Bad sa hodnota ignoruje.
D["DataValue"] --> V["Hodnota"]
D --> S["Stavový kód"]
D --> TS["SourceTimestamp (čas zdroja)"]
D --> TV["ServerTimestamp (čas servera)"]
S -->|"Ak je Bad"| X["Hodnotu ignorovať"]
Obrázok 5: Výsledok čítania obsahuje okrem hodnoty aj stavový kód a dve časové pečiatky.
Pri zápise som do Setpoint zapísal 32,5 a ako výsledný stavový kód sa vrátilo Good. Ktoré premenné sú zapisovateľné, určuje server. V tomto simulovanom serveri je zapisovateľná len Setpoint. Či povoliť zmenu nastavených hodnôt zariadenia zvonka, súvisí s bezpečnostným návrhom zariadenia, preto sa tomu tento článok nevenuje.
Oznamovanie zmien: odbery a MonitoredItem
Pri sledovaní zmien hodnôt môže klient opakovať Read v pravidelnom intervale. OPC UA má okrem toho mechanizmus, pri ktorom monitorovanie hodnôt prenecháte serveru a ten vám oznamuje zmeny. Tým mechanizmom sú odbery (subscription) a MonitoredItem.
MonitoredItem je jeden monitorovaný objekt a má interval vzorkovania. Interval vzorkovania je najrýchlejší interval, v ktorom server vzorkuje hodnoty zo zdroja, a špecifikácia ho označuje ako cyklus typu „best effort“11. Keď vzorka splní podmienky filtra, vytvorí sa oznámenie. Ak filter nie je zadaný, oznámenie sa odošle pri zmene hodnoty alebo stavu12.
Oznámenia sa najprv dostanú do fronty MonitoredItem. Vopred určíte, či sa pri pretečení fronty zahodí najstaršie, alebo najnovšie oznámenie, a ak je veľkosť fronty 2 alebo viac, v stavovom kóde sa nastaví bit Overflow, ktorý zahodenie signalizuje13.
Odber zoskupuje viacero MonitoredItem a v každom intervale publikovania posiela klientovi oznámenia, ktoré ešte neodoslal. Ak niekoľko cyklov nie je čo posielať, odošle správu keep-alive, ktorá oznamuje, že odber stále žije14.
flowchart TB
accTitle: Ako fungujú odbery a MonitoredItem
accDescr: Diagram, ktorý ukazuje, že MonitoredItem vzorkuje hodnotu zdroja v intervale vzorkovania a oznámenia, ktoré prejdú filtrom, vkladá do fronty, a odber ich v intervale publikovania posiela spolu klientovi, pričom keď oznámenia nie sú, posiela keep-alive.
S["Zdroj (hodnota zariadenia)"] --> M["MonitoredItem"]
M -->|"Interval vzorkovania"| F["Filter (predvolene zmena hodnoty alebo stavu)"]
F --> Q["Fronta (pri pretečení Overflow)"]
Q --> SUB["Odber"]
SUB -->|"Interval publikovania"| C["Klient"]
SUB -.->|"Ak nie sú oznámenia"| K["Keep-alive"]
Obrázok 6: MonitoredItem vzorkuje a ukladá do fronty, odber posiela oznámenia klientovi spolu.
Kód vyššie odoberá Temperature a PartCount s intervalom vzorkovania 500 milisekúnd a veľkosťou fronty 515. Vo výstupe prichádza teplota približne každých 0,5 sekundy a počet kusov približne každé 2 sekundy. Prvé dve oznámenia boli hodnoty, ktoré mali premenné v čase začatia odberu. Čas 13:40:50.801 pri počte kusov je čas, keď sa hodnota zmenila ešte pred začatím odberu. Keďže pri oznámeniach sa zobrazuje zdrojová časová pečiatka, vidno čas zmeny hodnoty, nie čas prijatia oznámenia.
Kedy opakované čítanie a kedy odber
Pri opakovanom čítaní určuje interval klient a mechanizmus je jednoduchý. Na druhej strane sa požiadavka a odpoveď posielajú zakaždým, aj keď sa hodnota nezmenila, a zmeny kratšie ako interval sa prehliadnu. Pri odbere server vzorkuje a posiela len zmeny, takže komunikácia veľmi nerastie ani pri hodnotách, ktoré sa menia zriedka. Aj pri odbere však zmeny kratšie ako interval vzorkovania nemusia byť oznámené. Špecifikácia rýchlejšie vzorkovanie, než je požadované, povoľuje, ale nevyžaduje11.
flowchart TB
accTitle: Rozdiel medzi opakovaným čítaním a odberom
accDescr: Diagram, ktorý ukazuje, že pri opakovanom čítaní určuje interval klient a zakaždým posiela požiadavku, pri odbere server vzorkuje v intervale vzorkovania a posiela len zmeny a že v oboch prípadoch sa zmeny kratšie ako interval nemusia zachytiť.
A["Sledovanie zmien hodnôt"] --> R["Opakované čítanie"]
A --> S["Odber"]
R --> R1["Klient určí interval a vždy pošle požiadavku"]
S --> S1["Server vzorkuje a posiela len zmeny"]
R1 --> X["Zmeny kratšie ako interval sa nemusia zachytiť"]
S1 --> X
Obrázok 7: Pri oboch spôsoboch sa zmeny kratšie ako interval nemusia zachytiť.
Povolenie pripojenia certifikátmi
Pri pripojení OPC UA sa úroveň ochrany správ volí cez MessageSecurityMode. Hodnoty sú None bez ochrany, Sign, ktorá len podpisuje, a SignAndEncrypt, ktorá podpisuje aj šifruje16. Kombináciu algoritmov na podpis a šifrovanie určuje bezpečnostná politika; v tomto článku som použil Basic256Sha256.
Na podpis a šifrovanie sa používa certifikát každej aplikácie. Klient a server si navzájom overujú certifikáty a odmietajú pripojenie od strany, ktorej nedôverujú. Server vytvorený knižnicou .NET od OPC Foundation predvolene neponúka nešifrovanú politiku None4.
Prvé pripojenie sa zastavilo na BadCertificateUntrusted
Pri prvom spustení klient certifikátu servera nedôveroval, preto bolo pripojenie odmietnuté. Toto je časť výstupu.
fail: Opc.Ua.CertificateValidationCore[91]
Certificate [Subject=CN=DemoPlcServer, CN=urn:komurasoft:demo-plc, Thumbprint=4E236DDA629FE2AB32D185BF681609FD6C525363, NotBefore=2026-11-01, NotAfter=2027-11-01, KeyAlgorithm=1.2.840.113549.1.1.1] validation failed with suppressible errors but was rejected. Reason=Id: BadCertificateUntrusted
...
OPC UA エラー BadCertificateUntrusted [0x801A0000]: [801A0000] (BadCertificateUntrusted) 'Could not verify security on OpenSecureChannel response.'
Táto knižnica ukladá odmietnuté certifikáty do úložiska rejected. Keď ich správca odtiaľ presunie do úložiska trusted, knižnica začne danej strane dôverovať17. Tentoraz som certifikát servera, ktorý bol v pki/rejected/certs, presunul do pki/trusted/certs, spustil klienta znova a pripojenie sa podarilo. Pred presunom overte so správcom servera odtlačok, ktorý je aj v názve súboru4.
flowchart TB
accTitle: Postup, kým certifikátu začnete dôverovať
accDescr: Diagram, ktorý ukazuje, že pri prvom pripojení klient odmietne certifikát servera a uloží ho do úložiska rejected a že po overení odtlačku a presune do úložiska trusted mu pri ďalšom pripojení dôveruje a pripojí sa.
A["Prvé pripojenie"] --> B["BadCertificateUntrusted"]
B --> C["Certifikát sa uloží do úložiska rejected"]
C --> D["Overiť odtlačok so správcom servera"]
D --> E["Presunúť do úložiska trusted"]
E --> F["Pri ďalšom pripojení mu dôveruje a pripojí sa"]
Obrázok 8: Ak odmietnutý certifikát overíte a presuniete do trusted, klient danej strane dôveruje.
Existuje aj nastavenie AutoAcceptUntrustedCertificates, ktoré nedôveryhodné certifikáty prijíma automaticky. Predvolená hodnota je false a dokumentácia knižnice uvádza, že vo vývojovom prostredí je užitočné, ale v produkcii by sa nemalo používať17.
NotAfter=2027-11-01 v zázname je dátum platnosti certifikátu simulovaného servera. Ide o certifikát s vlastným podpisom vytvorený funkciou asyncua a jeho platnosť skončila rok po dni vytvorenia. Aj certifikáty zariadení majú obmedzenú platnosť, preto si v rámci prevádzky stanovte postup, ako ich pred skončením platnosti vymeniť a zabezpečiť, aby im druhá strana dôverovala. Prácu s úložiskom certifikátov Windows opisuje článok „The Windows Certificate Store in Practice“.
Overenie používateľa
Certifikáty overujú aplikácie medzi sebou. Okrem toho sa pri otváraní relácie odovzdáva token, ktorý identifikuje používateľa. Špecifikácia pozná štyri druhy: anonymný, meno používateľa a heslo, certifikát X.509 a externe vydaný token18. Klient v tomto článku sa pripojil anonymne. Ktoré tokeny server prijíma, je uvedené v informáciách o koncovom bode servera19.
Kde sa zber údajov v praxi zasekne
Ďalej uvádzam problémy, ktoré často vznikajú v systémoch na zber údajov zo zariadení, na základe tohto pokusu a špecifikácie.
Hodnoty z obdobia, keď server stál, neprídu
Počas odberu som server zastavil a asi o 15 sekúnd ho znova spustil. ManagedSession tejto knižnice sa pri prerušení spojenia automaticky znova pripojí20. Kým server stál, klient opakovane skúšal opätovné pripojenie.
warn: Opc.Ua.ChannelManager[6]
Reconnect failed. Endpoint=opc.tcp://localhost:4840/demo/, Attempt=3, Outcome=transient-failure, Status=BadNotConnected, ErrorMessage=Could not connect to the remote endpoint.
Keď sa server vrátil, vrátili sa aj oznámenia. Toto je časť výstupu odberu.
13:41:13.364 Line1.Temperature 25.61 Good [0x00000000]
13:41:13.865 Line1.Temperature 25.12 Good [0x00000000]
13:41:34.285 Line1.Temperature 26.55 Good [0x00000000]
13:41:33.784 Line1.PartCount 2 Good [0x00000000]
13:41:34.786 Line1.Temperature 26.16 Good [0x00000000]
Z obdobia od 13:41:13 do 13:41:34 neprišla ani jedna hodnota. Počet kusov sa vrátil zo 78 na 2. Je to preto, že po opätovnom spustení začal simulovaný server počítať znova od 0.
V tomto príklade stál samotný server, preto hodnoty z toho obdobia nie sú nikde uložené. Mechanizmus opätovného pripojenia obnoví spojenie, ale hodnoty z obdobia výpadku dodatočne nedoplní.
Iná situácia je, keď sa preruší len komunikácia a server beží ďalej. Špecifikácia má službu Republish, ktorou si klient vyžiada, aby server znova poslal správy s oznámeniami, ktoré neprišli, zo svojej fronty na opakované odoslanie21. Oznámenia sa hromadia aj vo fronte MonitoredItem, ale to, čo presiahne veľkosť fronty, sa zahodí13. Tento prípad som v tomto pokuse neskúšal. V oboch prípadoch zaznamenávajte časy odpojenia a opätovného pripojenia, aby bolo vidieť, v ktorom období hodnoty neprišli. Opätovné pripojenie a návrh záznamov pri zariadeniach so sériovou komunikáciou opisuje aj článok „Serial Communication App Pitfalls“.
flowchart TB
accTitle: Záznam pri zastavení servera
accDescr: Diagram, ktorý ukazuje, že keď sa server počas odberu zastaví, klient opakovane skúša opätovné pripojenie a po návrate servera sa vrátia aj oznámenia, ale hodnoty z obdobia, keď server stál, neprídu, preto treba zaznamenať časy odpojenia a opätovného pripojenia.
A["Počas odberu"] --> B["Server sa zastaví"]
B --> C["Klient opakovane skúša opätovné pripojenie"]
C --> D["Server sa vráti a vrátia sa aj oznámenia"]
B -.-> E["Hodnoty z obdobia zastavenia neprídu"]
E -.-> F["Zaznamenať časy odpojenia a opätovného pripojenia"]
Obrázok 9: Opätovné pripojenie obnoví spojenie, ale hodnoty z obdobia, keď server stál, neprídu.
Ktorú časovú pečiatku zaznamenávať
Podľa špecifikácie je SourceTimestamp čas, ktorý hodnote priradil zdroj údajov, a mal by udávať čas zmeny hodnoty. ServerTimestamp je čas, keď server hodnotu prijal alebo zistil, že je správna9. Simulovaný server v tomto článku hodnoty vytvára sám, preto boli oba časy rovnaké.
Ak chcete vedieť, kedy sa hodnota na zariadení zmenila, použite SourceTimestamp. Tento čas však priraďujú hodiny zdroja, takže ak sa hodiny zariadenia alebo servera rozchádzajú, posunie sa aj záznam. Ak budete zozbierané údaje porovnávať s inými záznamami, ukladajte vedľa nich aj čas PC, ktoré ich prijalo, aby ste odchýlku mohli neskôr overiť. Synchronizáciu času na strane Windows opisuje článok „Windows Time Synchronization (w32time) and Business Systems“.
Hodnoty so stavom iným ako Good nepoužívajte ako obyčajné čísla
Keď je stavový kód Bad, hodnota sa má ignorovať9. Pri Uncertain ide o hodnotu, ktorá na niektoré účely nie je použiteľná10. Pred zaradením do grafov alebo súhrnov hodnoty roztrieďte podľa stavového kódu a vopred určite, ako narábať s hodnotami, ktoré nie sú Good. Aj bit Overflow pri pretečení fronty sa nastavuje v stavovom kóde13. Ako zobraziť stav zariadenia na obrazovke, opisuje článok „Best Practices for Checking and Displaying External Device State“.
Otvorenie portu v bráne firewall
Port OPC UA zaregistrovaný v IANA je 4840/TCP3. Nie všetky servery však toto číslo používajú. Ukážky .NET od OPC Foundation používajú porty ako 625414. Číslo portu overte v URL koncového bodu zariadenia a pripojenie na tento port povoľte. Ak server beží vo Windows, povoľte ho v bráne Windows Firewall daného počítača, a ak je medzi nimi sieťové zariadenie, tak aj na ňom. Vytváranie pravidiel pre prichádzajúcu komunikáciu opisuje článok „The Windows Firewall and Business Applications“.
flowchart TB
accTitle: Kde sa zber údajov zasekne
accDescr: Diagram, ktorý rozdeľuje miesta, kde sa zber údajov zo zariadení zasekne, na obsah záznamov (hodnoty z obdobia zastavenia servera neprídu, ktorú časovú pečiatku zaznamenávať, narábanie s hodnotami, ktoré nie sú Good) a prípravu pripojenia (porty v bráne firewall, platnosť certifikátov).
A["Kde sa zber údajov zasekne"] --> R["Obsah záznamov"]
A --> N["Príprava pripojenia"]
R --> B["Hodnoty počas zastavenia neprídu"]
B ~~~ C["Ktorá časová pečiatka"]
C ~~~ D["Hodnoty, ktoré nie sú Good"]
N --> E["Porty v bráne firewall"]
E ~~~ F["Platnosť certifikátov"]
Obrázok 10: Aj po úspešnom pripojení overte chýbajúce záznamy, časy, stavy, porty a certifikáty.
Kedy OPC UA a kedy Modbus/TCP alebo komunikácia výrobcu
Na komunikáciu s PLC a zariadeniami sa okrem OPC UA používa aj Modbus/TCP alebo komunikácia, ktorú poskytuje výrobca PLC.
Špecifikácia Modbus pracuje s údajmi v štyroch tabuľkách: Discrete Input (len na čítanie, 1 bit), Coil (čítanie aj zápis, 1 bit), Input Register (len na čítanie, 16 bitov) a Holding Register (čítanie aj zápis, 16 bitov)22. V IANA je ako aplikačný protokol Modbus (mbap) zaregistrovaný port 502/TCP3.
V týchto tabuľkách sú bity a 16-bitové slová, preto na ktorej adrese je ktorá hodnota a v akom formáte, treba overiť v dokumentácii zariadenia a zapísať na strane klienta. V OPC UA, ako sme videli, server zverejňuje pomenované premenné v adresnom priestore a klient ich môže zistiť cez Browse. K hodnotám patrí stavový kód a časové pečiatky a pripojenie sa povoľuje certifikátmi.
Čo použiť, často rozhodne to, čo zariadenie poskytuje. Ak má zariadenie server OPC UA, použite ho; ak má len Modbus/TCP, vytvorte tabuľku adries a čítajte podľa nej. Ak je k dispozícii len komunikácia alebo SDK výrobcu, postupujte podľa ich dokumentácie. Výber spôsobu komunikácie so zariadeniami USB opisuje článok „How to Work with USB Devices from a Windows App“.
flowchart TB
accTitle: Výber spôsobu komunikácie so zariadením
accDescr: Diagram, ktorý ukazuje, že ak má zariadenie server OPC UA, premenné sa zbierajú prehliadaním cez OPC UA, ak má len Modbus/TCP, v dokumentácii sa overia adresy a formáty a čítajú sa registre, a ak má len komunikáciu výrobcu, postupuje sa podľa jeho dokumentácie.
A["Komunikácia, ktorú zariadenie poskytuje"] --> B{"Čo je k dispozícii?"}
B -->|"Server OPC UA"| C["Zbierať premenné prehliadaním"]
B -->|"Len Modbus/TCP"| D["Overiť adresy a formáty v dokumentácii"]
B -->|"Len komunikácia výrobcu"| E["Postupovať podľa dokumentácie a SDK výrobcu"]
D --> F["Čítať registre a prepočítať na hodnoty"]
Obrázok 11: Čím údaje zbierať, často rozhodne to, čo zariadenie poskytuje.
Súvisiace články
- Dropped Frames with GigE Vision and USB3 Vision Cameras
- Serial Communication App Pitfalls
- Best Practices for Checking and Displaying External Device State
- How to Work with USB Devices from a Windows App
- The Windows Firewall and Business Applications
- The Windows Certificate Store in Practice
- Windows Time Synchronization (w32time) and Business Systems
Referenčné odkazy
-
OPC Foundation, Classic. To, že OPC Classic stojí na COM/DCOM, špecifikácie DA, A&E a HDA, zverejnenie OPC UA v roku 2008 a dôvody nového návrhu. ↩ ↩2
-
IEC, IEC TR 62541-1:2020 OPC Unified Architecture - Part 1: Overview and concepts. Časť 1 série IEC 62541 o koncepciách a prehľade OPC UA (medzičasom nahradená novším vydaním). ↩
-
IANA, Service Name and Transport Protocol Port Number Registry. Registrácia
opcua-tcpna 4840/TCP ambapna 502/TCP. ↩ ↩2 ↩3 -
OPC Foundation, UA-.NETStandard 2.0.1: Getting started. Ako napísať klienta verzie 2.0, to, že
ReadValueAsyncpri Bad vyhodí výnimku, úložiská odmietnutých a dôveryhodných certifikátov, to, že server predvolene neponúkaNone, a čísla portov v ukážkach. ↩ ↩2 ↩3 ↩4 ↩5 -
FreeOpcUa, asyncua (PyPI). Knižnica OPC UA pre Python použitá pre simulovaný server zariadenia. ↩
-
OPC Foundation, OPC 10000-4 v1.05, 5.9.2 Browse. Služba, ktorá prechádza odkazy uzlov. ↩
-
OPC Foundation, OPC 10000-3 v1.05, 8.2.2 NamespaceIndex. To, že URI menného priestoru sa v rámci služieb vyjadruje číslom a toto číslo zodpovedá pozícii v NamespaceArray. ↩
-
OPC Foundation, OPC 10000-4 v1.05, 5.11 Attribute Service Set. Služby Read a Write. ↩
-
OPC Foundation, OPC 10000-4 v1.05, 7.11 DataValue. Definície hodnoty, stavového kódu, SourceTimestamp a ServerTimestamp. ↩ ↩2 ↩3
-
OPC Foundation, OPC 10000-4 v1.05, 7.38.1 StatusCode General. Good, Uncertain a Bad podľa horných dvoch bitov. ↩ ↩2
-
OPC Foundation, OPC 10000-4 v1.05, 5.13.1.2 Sampling interval. To, že interval vzorkovania je cyklus typu best effort. ↩ ↩2
-
OPC Foundation, OPC 10000-4 v1.05, 7.22.2 DataChangeFilter. To, že bez filtra sa oznámenie posiela pri zmene hodnoty alebo stavu. ↩
-
OPC Foundation, OPC 10000-4 v1.05, 5.13.1.5 Queue parameters. Veľkosť fronty, spôsob zahadzovania a bit Overflow. ↩ ↩2 ↩3
-
OPC Foundation, OPC 10000-4 v1.05, 5.14.1.1 Subscription model. Interval publikovania a keep-alive. ↩
-
OPC Foundation, UA-.NETStandard 2.0.1: Subscriptions and Monitored Items Service Set.
SubscribeDataChangesAsynca interval vzorkovania a veľkosť fronty vMonitoredItemOptions. ↩ -
OPC Foundation, OPC 10000-4 v1.05, 7.20 MessageSecurityMode. Význam None, Sign a SignAndEncrypt. ↩
-
OPC Foundation, UA-.NETStandard 2.0.1: Certificates. Postup presunu certifikátov z úložiska rejected do dôveryhodného úložiska a to, že
AutoAcceptUntrustedCertificatessa v produkcii nemá používať. ↩ ↩2 -
OPC Foundation, OPC 10000-4 v1.05, 7.40 UserIdentityToken parameters. Štyri druhy: anonymný, meno používateľa, X.509 a vydaný token. ↩
-
OPC Foundation, OPC 10000-4 v1.05, 7.14 EndpointDescription. Zoznam tokenov používateľov, ktoré koncový bod prijíma (userIdentityTokens). ↩
-
OPC Foundation, UA-.NETStandard 2.0.1: Sessions, Reconnection, and Subscription Engines. To, že
ManagedSessionmá zabudovaný mechanizmus opätovného pripojenia. ↩ -
OPC Foundation, OPC 10000-4 v1.05, 5.14.6.1 Republish. Služba, ktorou si klient vyžiada opätovné odoslanie správ s oznámeniami z fronty odberu na opakované odoslanie. ↩
-
Modbus-IDA, MODBUS Application Protocol Specification V1.1b (kópia na serveri Chicagskej univerzity). Dátový model v časti 4.3 (Discrete Input, Coil, Input Register, Holding Register). ↩
Súvisiace články
Nedávne články s rovnakými štítkami. Prehĺbte si tému pomocou úzko súvisiacich textov.
Čo sa zastaví, keď zapnete „pripojenie účtované podľa objemu údajov“? ── Vplyv na Windows Update, OneDrive a aplikácie z obchodu
Keď vo Windowse zapnete pripojenie účtované podľa objemu údajov, Windows Update sťahuje len prioritné aktualizácie a OneDrive pozastaví s...
Možno odstrániť Windows.old a „Dočasné súbory“? ── Kedy použiť Senzor úložiska a kedy Čistenie disku
O Windows.old a dočasných súboroch rozhoduje, či po odstránení niečo nestratíte natrvalo. 10 dní na návrat k predchádzajúcemu Windowsu, S...
Čo mení režim napájania vo Windowse? ── Rozdiel medzi režimami Najlepší výkon, Vyvážený a Najlepšia energetická účinnosť a ako si vybrať
Vysvetľujeme, čo menia režimy napájania Windowsu 11 Najlepší výkon, Vyvážený a Najlepšia energetická účinnosť: nastavenia CPU v pozadí, v...
Čo je „Herný režim“ vo Windowse? ── Čo sa zmení, keď ho zapnete, a ovplyvňuje aj niečo iné ako hry?
Herný režim vo Windowse počas spustenej hry zastaví inštaláciu ovládačov a oznámenia o reštartovaní zo služby Windows Update a snaží sa o...
Čo je „vyhradená pamäť GPU“ a „zdieľaná pamäť GPU“ v Správcovi úloh? ── Prečo sa čísla pri integrovanom GPU tak líšia a čo sa stane, keď pamäť nestačí
Bežným používateľom vysvetľujeme, čo znamená vyhradená a zdieľaná pamäť GPU v Správcovi úloh. Zhŕňame, prečo je pri integrovanom GPU vyhr...
Súvisiace témy
Toto sú stránky tém blízke téme tohto článku. Od článku môžete pokračovať k súvisiacim službám a ďalším článkom.
Technické témy o Windows
Úvodná stránka k technickým témam o vývoji pre Windows, vyšetrovaní chýb a využití existujúcich riešení.
Služby, s ktorými táto téma súvisí
Tento článok nadväzuje na nasledujúce stránky služieb. Začnite tou, ktorá je vám najbližšia.
Vývoj aplikácií pre Windows
Vývoj aplikácie pre Windows, ktorá zbiera údaje zo zariadení, vrátane nastavenia odberov, opätovného pripojenia a zaznamenávania časových pečiatok a stavu, môžeme riešiť ako vývoj aplikácií pre Windows.
Technické konzultácie a revízia návrhu
Ak sa chcete rozhodnúť, či zbierať údaje cez OPC UA, alebo použiť Modbus/TCP či komunikáciu výrobcu, a ako spravovať certifikáty, môžeme to riešiť ako technické konzultácie a revíziu návrhu.
Často kladené otázky
Zhrnuli sme otázky k téme tohto článku, ktoré sa pri konzultáciách objavujú najčastejšie.
- Aký je rozdiel medzi OPC UA a OPC Classic (OPC DA)?
- OPC Classic sú špecifikácie, ktoré si vymieňajú údaje cez COM/DCOM vo Windows. OPC UA je špecifikácia zverejnená v roku 2008, ktorá nezávisí od konkrétneho operačného systému a spája funkcie Classic. Klienti OPC UA bežia aj mimo Windows a nepotrebujú nastavenie DCOM.
- Stačí pri zbere hodnôt cez OPC UA čítať v pravidelnom intervale?
- Ak je hodnôt málo a interval je dlhý, dajú sa zbierať aj čítaním. Ak chcete sledovať zmeny hodnôt, môžete použiť odber, pri ktorom vzorkovanie prenecháte serveru a ten vám zmeny oznamuje. V oboch prípadoch zaznamenávajte aj stavový kód a časové pečiatky, ktoré prichádzajú spolu s hodnotou.
- Prečo pri pripojení dostávam BadCertificateUntrusted?
- Pretože klient ešte nedôveruje certifikátu servera. Knižnica .NET od OPC Foundation ukladá odmietnuté certifikáty do úložiska rejected. Odtlačok certifikátu overte so správcom servera a potom certifikát presuňte do úložiska trusted. Nastavenie, ktoré certifikáty prijíma automaticky, používajte len vo vývojovom prostredí.
- Aké číslo portu používa OPC UA?
- V IANA je ako opcua-tcp zaregistrovaný port 4840/TCP. Skutočné servery však niekedy používajú iný port, preto ho overte v nastaveniach zariadenia. Ak server beží vo Windows, komunikáciu na tento port treba povoliť v bráne Windows Firewall, a ak je medzi nimi sieťové zariadenie, tak aj na ňom.
Profil autora
Profilová stránka autora článku.
Go Komura
Zástupca spoločnosti KomuraSoft LLC
Zameriava sa najmä na vývoj softvéru pre Windows, technické konzultácie a vyšetrovanie chýb; má silné stránky v zákazkách, kde zostávajú existujúce aktíva, a pri vyšetrovaní porúch, ktorých príčina nie je dobre viditeľná.