# Fehler behandeln

> Anleitung: Wann der Client ein Ergebnis mit Status liefert und wann er eine OpcException auslöst, wie Sie beides auswerten und welche Statuscodes häufig sind.

Der Client unterscheidet zwei Arten von Fehlern:

- **Fehler einzelner Operationen** meldet er über einen Status im Ergebnis. Ein gelesener Wert, das Ergebnis eines Schreibvorgangs oder eines Browse-Pfads trägt jeweils einen eigenen Status. Wenn eine von vielen Operationen fehlschlägt, erhalten die übrigen trotzdem ihr Ergebnis.
- **Fehler des ganzen Aufrufs** meldet er über eine `OpcException`. Das ist der Fall, wenn keine Verbindung besteht, der Server die Anfrage insgesamt ablehnt oder ein Methodenaufruf fehlschlägt.

## Status auswerten

Beteiligte Typen: `OpcValue`, `OpcStatus`, `OpcStatusCode`

Ein `OpcStatus` teilt Ergebnisse in drei Gruppen ein: `IsGood`, `IsUncertain` und `IsBad`. Werte mit unsicherem Status liefern einen Wert, dessen Qualität der Server nicht garantiert, zum Beispiel den letzten brauchbaren Wert einer ausgefallenen Quelle. `Code` liefert den genauen `OpcStatusCode`, `Description` eine lesbare Beschreibung:

```csharp
OpcValue value = client.ReadNode("ns=2;s=Machine/Speed");
 
if (value.Status.IsGood)
    Console.WriteLine("Speed: {0}", value.Value);
else if (value.Status.IsUncertain)
    Console.WriteLine("Speed (uncertain): {0}", value.Value);
else
    Console.WriteLine("Read failed: {0} ({1})", value.Status.Code, value.Status.Description);

```

```vbnet
Dim value As OpcValue = client.ReadNode("ns=2;s=Machine/Speed")

If value.Status.IsGood Then
    Console.WriteLine("Speed: {0}", value.Value)
ElseIf value.Status.IsUncertain Then
    Console.WriteLine("Speed (uncertain): {0}", value.Value)
Else
    Console.WriteLine("Read failed: {0} ({1})", value.Status.Code, value.Status.Description)
End If
```

Schreibvorgänge liefern direkt einen `OpcStatus` beziehungsweise eine `OpcStatusCollection`. Über `Code` lassen sich einzelne Fehlerursachen gezielt behandeln:

```csharp
OpcStatus result = client.WriteNode("ns=2;s=Machine/Setpoint", value: 42.0);
 
if (result.Code == OpcStatusCode.BadUserAccessDenied)
    Console.WriteLine("The user is not allowed to write the setpoint.");
else if (result.IsBad)
    Console.WriteLine("Write failed: {0}", result.Description);

```

```vbnet
Dim result As OpcStatus = client.WriteNode("ns=2;s=Machine/Setpoint", value:=42.0)

If result.Code = OpcStatusCode.BadUserAccessDenied Then
    Console.WriteLine("The user is not allowed to write the setpoint.")
ElseIf result.IsBad Then
    Console.WriteLine("Write failed: {0}", result.Description)
End If
```

Bei Aufrufen mit vielen Befehlen gehört jedes Ergebnis zu dem Befehl mit demselben Index, siehe [Viele Werte mit einem Aufruf schreiben](https://www.traeger.de/docs/development/opcua/client/bulk-access#viele-werte-mit-einem-aufruf-schreiben).

## Ausnahmen behandeln

Beteiligte Typen: `OpcClient`, `OpcDiscoveryClient`, `OpcException`, `OpcStatusCode`

Eine `OpcException` trägt den Statuscode des Fehlers in ihrer Eigenschaft `Code`. Methodenaufrufe lösen sie aus, sobald der Server den Aufruf ablehnt oder eines der Eingabeargumente als ungültig meldet:

```csharp
try {
    object[] outputs = client.CallMethod(
            "ns=2;s=Machine",
            "ns=2;s=Machine/StartJob",
            "Job 1");
}
catch (OpcException ex) {
    Console.WriteLine("Call failed: {0} ({1})", ex.Code, ex.Message);
}
```

```vbnet
Try
    Dim outputs As Object() = client.CallMethod( _
            "ns=2;s=Machine", _
            "ns=2;s=Machine/StartJob", _
            "Job 1")
Catch ex As OpcException
    Console.WriteLine("Call failed: {0} ({1})", ex.Code, ex.Message)
End Try

```

Auch `Connect` meldet Fehler als `OpcException`, zum Beispiel wenn der Server nicht erreichbar ist oder kein Endpunkt zu den Sicherheitseinstellungen des Clients passt. Danach ist der Client wieder im vorherigen Zustand:

```csharp
try {
    client.Connect();
}
catch (OpcException ex) {
    if (ex.Code == OpcStatusCode.BadSecurityChecksFailed)
        Console.WriteLine("No endpoint matches the security settings of the client.");
    else
        Console.WriteLine("Connect failed: {0}", ex.Code);
}
```

```vbnet
Try
    client.Connect()
Catch ex As OpcException
    If ex.Code = OpcStatusCode.BadSecurityChecksFailed Then
        Console.WriteLine("No endpoint matches the security settings of the client.")
    Else
        Console.WriteLine("Connect failed: {0}", ex.Code)
    End If
End Try
```

Dasselbe gilt für `OpcDiscoveryClient`: Wenn der Server nicht erreichbar ist, den Kanal beim Verbindungsaufbau schließt oder das Schema der Discovery-Adresse nicht unterstützt wird, lösen `DiscoverEndpoints`, `DiscoverServers` und die Konstruktoren eine `OpcException` mit dem Grund aus, zum Beispiel `BadSecureChannelClosed`.

Neben `OpcException` treten die üblichen .NET-Ausnahmen auf: `ArgumentNullException` für fehlende Argumente, `InvalidOperationException`, wenn ein Aufruf im aktuellen Zustand nicht möglich ist (zum Beispiel `Connect` ohne Serveradresse), und `ObjectDisposedException` nach `Dispose`. Wenn die Anwendung einen Dienst aufruft, während keine Verbindung besteht, löst der Client eine `OpcException` mit `BadServerNotConnected` aus.

## Häufige Statuscodes

| Statuscode | Bedeutung | Typische Ursache |
|---|---|---|
| `BadNodeIdUnknown` | Der Node existiert im Adressraum des Servers nicht. | falscher Identifier oder Namespace-Index, siehe [Nodes über die Namespace-URI adressieren](https://www.traeger.de/docs/development/opcua/client/read-write-values#nodes-uber-die-namespace-uri-adressieren) |
| `BadNodeIdInvalid` | Die Syntax des Identifiers ist ungültig. | Tippfehler im NodeId-String |
| `BadNotReadable`, `BadNotWritable` | Die Zugriffsrechte (AccessLevel) des Nodes erlauben das Lesen beziehungsweise Schreiben nicht. | schreibgeschützte Variable |
| `BadUserAccessDenied` | Der Benutzer der Sitzung hat keine Berechtigung für die Operation. | Benutzeridentität mit zu wenig Rechten, siehe [Benutzer anmelden](https://www.traeger.de/docs/development/opcua/security/client#benutzer-anmelden) |
| `BadTypeMismatch` | Der geschriebene Wert hat nicht den Datentyp des Nodes. | zum Beispiel `int` statt `double` geschrieben |
| `BadOutOfRange` | Der Wert liegt außerhalb des zulässigen Bereichs. | Grenzwert des Servers überschritten |
| `BadWriteNotSupported` | Der Server unterstützt die Kombination aus Wert, Status und Zeitstempeln nicht. | siehe [Zeitstempel beim Schreiben](https://www.traeger.de/docs/development/opcua/client/read-write-values#zeitstempel-beim-schreiben) |
| `BadIndexRangeInvalid` | Die Syntax des Teilbereichs ist ungültig. | falscher `OpcRange` |
| `BadNoMatch` | Ein Browse-Pfad lässt sich nicht auflösen. | falscher Pfad bei `TranslatePaths` |
| `BadTooManyOperations` | Die Anfrage enthält zu viele Operationen. | eigene Grenze zu hoch gesetzt, siehe [Wie das SDK große Anfragen aufteilt](https://www.traeger.de/docs/development/opcua/client/bulk-access#wie-das-sdk-gro%C3%9Fe-anfragen-aufteilt) |
| `BadEncodingLimitsExceeded` | Die Grenzen für das Kodieren oder Dekodieren wurden überschritten. | sehr große Werte, siehe [Grenzen der Datenübertragung](https://www.traeger.de/docs/development/opcua/client/configuration#grenzen-der-datenubertragung) |
| `BadTimeout` | Die Operation hat das Zeitlimit überschritten. | `OperationTimeout` zu kurz oder Server überlastet |
| `BadServerNotConnected` | Der Client ist nicht mit dem Server verbunden. | Aufruf vor `Connect` oder während eines Abbruchs |
| `BadSecurityChecksFailed` | Bei der Prüfung der Sicherheit ist ein Fehler aufgetreten. | kein passender Endpunkt oder abgelehntes Zertifikat |
| `BadIdentityTokenRejected` | Der Server hat die Benutzeridentität abgelehnt. | falscher Benutzername oder falsches Passwort |
| `BadArgumentsMissing`, `BadInvalidArgument` | Eingabeargumente eines Methodenaufrufs fehlen oder sind ungültig. | Anzahl oder Typen der Argumente stimmen nicht |
| `UncertainLastUsableValue` | Die Quelle des Werts aktualisiert ihn nicht mehr. | ausgefallene Steuerung hinter dem Server |

## Statuscodes aus Sicht des Servers

Ein Server, der mit dem OPC UA .NET SDK entwickelt ist, meldet einige Statuscodes selbst oder auf Veranlassung Ihres Codes. Die folgenden Fälle beschreiben die Seiten der Server-Entwicklung:

| Statuscode | Wann der Server ihn meldet | Mehr dazu |
|---|---|---|
| `BadUserAccessDenied` | Ein Benutzer meldet sich über einen Endpunkt an, der für seinen Zugriffskontrolleintrag nicht erteilt ist. Außerdem bei zu vielen gleichzeitigen Anmeldeversuchen und bei Aufrufen der Zertifikatsverwaltung über GDS, deren Bedingungen nicht erfüllt sind. | [Benutzer auf Endpunkte beschränken](https://www.traeger.de/docs/development/opcua/security/server#benutzer-auf-endpunkte-beschranken), [Anmeldeversuche begrenzen](https://www.traeger.de/docs/development/opcua/security/server#anmeldeversuche-begrenzen), [Zertifikate per GDS verwalten](https://www.traeger.de/docs/development/opcua/security/gds-push#voraussetzungen) |
| `BadNotSupported` | Ein Eventhandler der Anfrageverarbeitung verweigert eine Anfrage, indem er die Eigenschaft `Cancel` der Ereignisargumente auf `true` setzt. | [Anfragen validieren](https://www.traeger.de/docs/development/opcua/server/request-monitoring#anfragen-validieren) |
| `BadCommunicationError` | Ihr Code meldet ihn an einem Node, wenn die Steuerung hinter dem Server nicht erreichbar ist. Der Node behält seinen letzten Wert. | [Werte aktualisieren](https://www.traeger.de/docs/development/opcua/server/device-gateway#werte-aktualisieren) |
| `BadAggregateNotSupported` | Ein Client fordert ein Aggregat an, das der Server nicht unterstützt. | [Aggregierte Werte und Werte zu Zeitpunkten](https://www.traeger.de/docs/development/opcua/server/historical-data#aggregierte-werte-und-werte-zu-zeitpunkten) |
| `BadNoData` | Ein Client liest einen Wert zu einem Zeitpunkt, vor dem es keinen Rohwert gibt. | [Aggregierte Werte und Werte zu Zeitpunkten](https://www.traeger.de/docs/development/opcua/server/historical-data#aggregierte-werte-und-werte-zu-zeitpunkten) |
| `UncertainDataSubNormal` | Der Server hat bei der Berechnung eines Aggregats Rohwerte mit schlechtem Status übersprungen. | [Aggregierte Werte und Werte zu Zeitpunkten](https://www.traeger.de/docs/development/opcua/server/historical-data#aggregierte-werte-und-werte-zu-zeitpunkten) |
| `BadEncodingLimitsExceeded` | Ein Wert überschreitet die Grenzen, die der Server für den Datenaustausch festlegt. | [Grenzen der Datenübertragung](https://www.traeger.de/docs/development/opcua/server/configuration#grenzen-der-datenubertragung) |
