Zum Inhalt springen

Anleitung

Dateien lesen und schreiben

Auf Datei-Nodes eines Servers zugreifen: Inhalte lesen und anhängen, Dateien als Stream öffnen, Rechte prüfen und Fehler behandeln.

Anleitung

Dateien lesen und schreiben ​

Beteiligte TypenOpcClientOpcNodeIdOpcFileOpcFileModeOpcFileStreamOpcFileInfoOpcFileMethodsSafeOpcFileHandle

Nodes vom Typ FileType haben laut OPC-UA-Spezifikation festgelegte Eigenschaften (Property-Nodes) und Methoden (Methoden-Nodes). Über sie greift der Client auf einen Datenstrom so zu wie auf eine Datei im Dateisystem. Der Node stellt dabei nur Informationen über den Inhalt der logischen oder physischen Datei bereit. Einen eventuell vorhandenen Pfad zur Datei stellt er laut Spezifikation nicht bereit. Auf die Datei selbst greift der Client über die Methoden Open, Close, Read, Write, GetPosition und SetPosition zu. Diese Methoden verarbeiten die Daten immer binär. Wie auf jeder anderen Plattform geben Sie auch in OPC UA beim Aufruf von Open einen Modus an, der die Art des geplanten Dateizugriffs festlegt. Auch in OPC UA können Sie exklusiven Zugriff auf eine Datei anfordern. Nach dem Aufruf von Open erhält der Client einen numerischen Schlüssel für den weiteren Dateizugriff (engl. File Handle). Diesen Schlüssel übergeben Sie bei den Methoden Read, Write, GetPosition und SetPosition immer mit. Schließen Sie eine geöffnete Datei mit Close, sobald Sie sie nicht mehr benötigen.

Auf Nodes vom Typ FileType können Sie manuell über die Methoden ReadNode und CallMethod der Klasse OpcClient zugreifen. Alternativ bietet das SDK zahlreiche weitere Klassen, die den Zugriff auf solche Nodes unter anderem nach dem Vorbild von .NET umsetzen. Die NodeId legt fest, auf welchen Datei-Node der Client zugreift.

Die passende Klasse wählen ​

KlasseVorbild in .NETGeeignet für
OpcFileSystem.IO.Fileganze Inhalte in einem Aufruf lesen oder anhängen (ReadAllText, ReadAllLines, ReadAllBytes, AppendAllText, AppendAllLines) und Streams öffnen (Open, OpenRead, OpenWrite, OpenText, AppendText)
OpcFileInfo (Namespace Opc.UaFx.Client)System.IO.FileInfoEigenschaften einer Datei abfragen (Exists, Length, Name, MimeType, CanWrite, CanUserWrite) und die Datei öffnen
OpcFileStreamSystem.IO.FileStreamLesen, Schreiben und Positionieren über die Schnittstelle von Stream, auch zusammen mit StreamReader, StreamWriter oder CopyTo
OpcFileMethodskeinesdie Methoden des Datei-Nodes (Open, Read, Write, GetPosition, SetPosition, Close) einzeln aufrufen

Den Modus beim Öffnen legt OpcFileMode fest: Read, Write, ReadWrite, EraseExisting und Truncate (Schreiben nach Leeren der Datei). Methoden zum Überschreiben einer ganzen Datei in einem Aufruf (wie File.WriteAllText) bietet OpcFile nicht. Zum Schreiben verwenden Sie einen mit OpenWrite oder Open geöffneten Stream.

Ganze Dateien lesen und schreiben ​

Gesamten Inhalt einer Textdatei lesen:

csharp
string reportText = OpcFile.ReadAllText(client, "ns=2;s=Machine/Report");
vbnet
Dim reportText As String = OpcFile.ReadAllText(client, "ns=2;s=Machine/Report")

Weitere Textdaten an eine Textdatei anhängen:

csharp
OpcFile.AppendAllText(client, "ns=2;s=Machine/Report", "Lorem ipsum");
vbnet
OpcFile.AppendAllText(client, "ns=2;s=Machine/Report", "Lorem ipsum")

Datei mit OpcFileStream öffnen und lesen:

csharp
using (var stream = OpcFile.OpenRead(client, "ns=2;s=Machine/Report")) {
    var reader = new StreamReader(stream);
 
    while (!reader.EndOfStream)
        Console.WriteLine(reader.ReadLine());
}
vbnet
Using stream = OpcFile.OpenRead(client, "ns=2;s=Machine/Report")
    Dim reader = New StreamReader(stream)

    While Not reader.EndOfStream
        Console.WriteLine(reader.ReadLine())
    End While
End Using

Datei mit OpcFileStream öffnen und schreiben:

csharp
using (var stream = OpcFile.OpenWrite(client, "ns=2;s=Machine/Report")) {
    var writer = new StreamWriter(stream);
 
    writer.WriteLine("Lorem ipsum");
    writer.WriteLine("dolor sit");
    // ...
}
vbnet
Using stream = OpcFile.OpenWrite(client, "ns=2;s=Machine/Report")
    Dim writer = New StreamWriter(stream)

    writer.WriteLine("Lorem ipsum")
    writer.WriteLine("dolor sit")
    '...
End Using

Den gesamten Inhalt als Zeilen oder als Bytes lesen:

csharp
string[] lines = OpcFile.ReadAllLines(client, "ns=2;s=Machine/Recipe");
byte[] content = OpcFile.ReadAllBytes(client, "ns=2;s=Machine/Snapshot");
vbnet
Dim lines As String() = OpcFile.ReadAllLines(client, "ns=2;s=Machine/Recipe")
Dim content As Byte() = OpcFile.ReadAllBytes(client, "ns=2;s=Machine/Snapshot")

Dateiinformationen abfragen und öffnen ​

Die Klasse OpcFileInfo des Namespace Opc.UaFx.Client beschreibt eine einzelne Datei. Auch der Namespace Opc.UaFx enthält eine Klasse OpcFileInfo. Die folgenden Beispiele geben den Typnamen deshalb voll qualifiziert an:

Instanz von OpcFileInfo erstellen:

csharp
// The type name is qualified, because the Opc.UaFx namespace declares an OpcFileInfo too.
var file = new Opc.UaFx.Client.OpcFileInfo(client, "ns=2;s=Machine/Report");
vbnet
'The type name is qualified, because the Opc.UaFx namespace declares an OpcFileInfo too.
Dim file = New Opc.UaFx.Client.OpcFileInfo(client, "ns=2;s=Machine/Report")

Mit der Instanz von OpcFileInfo arbeiten:

csharp
if (file.Exists) {
    Console.WriteLine("File Length: {0}", file.Length);
 
    if (file.CanUserWrite) {
        using (var stream = file.OpenWrite()) {
            // Your code to write via stream.
        }
    }
    else {
        using (var stream = file.OpenRead()) {
            // Your code to read via stream.
        }
    }
}
vbnet
If file.Exists Then
    Console.WriteLine("File Length: {0}", file.Length)

    If file.CanUserWrite Then
        Using stream = file.OpenWrite()
            'Your code to write via stream.
        End Using
    Else
        Using stream = file.OpenRead()
            'Your code to read via stream.
        End Using
    End If
End If

Eigenschaften abfragen und nur schreiben, wenn der Benutzer es darf: CanWrite gibt an, ob die Datei grundsätzlich beschreibbar ist. CanUserWrite gibt an, ob der angemeldete Benutzer sie beschreiben darf. Ein zum Schreiben geöffneter OpcFileStream berücksichtigt beides. Seine Eigenschaft CanWrite ist nur dann true, wenn beides zutrifft:

csharp
var file = new Opc.UaFx.Client.OpcFileInfo(client, "ns=2;s=Machine/Recipe");
 
Console.WriteLine(
        "{0}: {1} bytes, {2}",
        file.Name,
        file.Length,
        file.MimeType);
 
if (file.Exists && file.CanUserWrite) {
    using (var stream = file.OpenWrite())
    using (var writer = new StreamWriter(stream)) {
        writer.Write("Speed=1200");
    }
}
vbnet
Dim file = New Opc.UaFx.Client.OpcFileInfo(client, "ns=2;s=Machine/Recipe")

Console.WriteLine( _
        "{0}: {1} bytes, {2}", _
        file.Name, _
        file.Length, _
        file.MimeType)

If file.Exists AndAlso file.CanUserWrite Then
    Using stream = file.OpenWrite()
        Using writer = New StreamWriter(stream)
            writer.Write("Speed=1200")
        End Using
    End Using
End If

Große Dateien als Stream verarbeiten ​

Große Dateien kopieren: Da OpcFileStream von Stream abgeleitet ist, kopieren Sie eine Datei des Servers mit CopyTo in eine lokale Datei. Der Stream liest den Inhalt dabei abschnittsweise:

csharp
using (var source = OpcFile.OpenRead(client, "ns=2;s=Machine/Snapshot"))
using (var target = File.Create("snapshot.bin")) {
    source.CopyTo(target);
}
vbnet
Using source = OpcFile.OpenRead(client, "ns=2;s=Machine/Snapshot")
    Using target = IO.File.Create("snapshot.bin")
        source.CopyTo(target)
    End Using
End Using

An eine bestimmte Position schreiben: Mit Seek legen Sie die Position innerhalb der Datei fest, zum Beispiel ans Ende, um einen Eintrag anzuhängen:

csharp
using (var stream = OpcFile.Open(client, "ns=2;s=Machine/Log", OpcFileMode.ReadWrite)) {
    stream.Seek(0, SeekOrigin.End);
 
    byte[] entry = Encoding.UTF8.GetBytes("Job started" + Environment.NewLine);
    stream.Write(entry, 0, entry.Length);
}
vbnet
Using stream = OpcFile.Open(client, "ns=2;s=Machine/Log", OpcFileMode.ReadWrite)
    stream.Seek(0, SeekOrigin.End)

    Dim entry As Byte() = Encoding.UTF8.GetBytes("Job started" & Environment.NewLine)
    stream.Write(entry, 0, entry.Length)
End Using

Dateimethoden direkt aufrufen ​

Über das .NET-Konzept SafeHandle (Klasse SafeOpcFileHandle):

csharp
using (var handle = OpcFileMethods.SecureOpen(client, "ns=2;s=Machine/Report", OpcFileMode.ReadWrite)) {
    byte[] data = OpcFileMethods.SecureRead(handle, 100);
 
    long position = OpcFileMethods.SecureGetPosition(handle);
    OpcFileMethods.SecureSetPosition(handle, position + data[data.Length - 1]);
 
    OpcFileMethods.SecureWrite(handle, new byte[] { 1, 2, 3 });
}
vbnet
Using handle = OpcFileMethods.SecureOpen(client, "ns=2;s=Machine/Report", OpcFileMode.ReadWrite)
    Dim data As Byte() = OpcFileMethods.SecureRead(handle, 100)

    Dim position As Long = OpcFileMethods.SecureGetPosition(handle)
    OpcFileMethods.SecureSetPosition(handle, position + data(data.Length - 1))

    OpcFileMethods.SecureWrite(handle, New Byte() {1, 2, 3})
End Using

Über einen numerischen File Handle:

csharp
uint handle = OpcFileMethods.Open(client, "ns=2;s=Machine/Report", OpcFileMode.ReadWrite);
 
try {
    byte[] data = OpcFileMethods.Read(client, "ns=2;s=Machine/Report", handle, 100);
 
    ulong position = OpcFileMethods.GetPosition(client, "ns=2;s=Machine/Report", handle);
    OpcFileMethods.SetPosition(client, "ns=2;s=Machine/Report", handle, position + data[data.Length - 1]);
 
    OpcFileMethods.Write(client, "ns=2;s=Machine/Report", handle, new byte[] { 1, 2, 3 });
}
finally {
    OpcFileMethods.Close(client, "ns=2;s=Machine/Report", handle);
}
vbnet
Dim handle As UInteger = OpcFileMethods.Open(client, "ns=2;s=Machine/Report", OpcFileMode.ReadWrite)

Try
    Dim data As Byte() = OpcFileMethods.Read(client, "ns=2;s=Machine/Report", handle, 100)

    Dim position As ULong = OpcFileMethods.GetPosition(client, "ns=2;s=Machine/Report", handle)
    OpcFileMethods.SetPosition(client, "ns=2;s=Machine/Report", handle, position + data(data.Length - 1))

    OpcFileMethods.Write(client, "ns=2;s=Machine/Report", handle, New Byte() {1, 2, 3})
Finally
    OpcFileMethods.Close(client, "ns=2;s=Machine/Report", handle)
End Try

Nur Dateizugriffe über OpcFile, OpcFileInfo, OpcFileStream und SafeOpcFileHandle garantieren, dass eine geöffnete Datei auch dann freigegeben wird, wenn der Aufruf der Methode Close fehlt. Spätestens beim Schließen der Verbindung zum Server schließt der OpcClient dann alle geöffneten Dateien automatisch. Das gilt nicht, wenn Sie die Methoden der Klasse OpcFileMethods ohne das Präfix Secure verwenden.

Die OPC-UA-Spezifikation definiert keinen Weg, einen Node als Node vom Typ FileType und damit als Datei-Node zu erkennen. Das SDK kann einen Datei-Node deshalb anhand seiner Node-Struktur identifizieren:

csharp
if (OpcFileMethods.IsFileNode(client, "ns=2;s=Machine/Report")) {
    // Your code to operate on the file node.
}
vbnet
If OpcFileMethods.IsFileNode(client, "ns=2;s=Machine/Report") Then
    'Your code to operate on the file node.
End If

Fehler behandeln ​

Wenn der angegebene Node nicht existiert oder kein Datei-Node ist, lösen die Methoden von OpcFile, OpcFileInfo und OpcFileMethods eine InvalidOperationException aus. Wenn der Server einen Methodenaufruf ablehnt, löst die Methode eine OpcException mit dem Status des Servers aus. Das ist zum Beispiel der Fall, wenn der Benutzer die Datei nicht beschreiben darf oder die Datei bereits exklusiv geöffnet ist. Operationen, die der Modus des Streams nicht zulässt (zum Beispiel Schreiben in einen zum Lesen geöffneten Stream), quittiert OpcFileStream mit einer NotSupportedException:

csharp
try {
    string recipe = OpcFile.ReadAllText(client, "ns=2;s=Machine/Recipe");
}
catch (InvalidOperationException) {
    // The node does not exist or is not a file node.
}
catch (OpcException ex) {
    // The server rejected a file method, e.g. with BadUserAccessDenied.
    Console.WriteLine("{0}: {1}", ex.Code, ex.Message);
}
vbnet
Try
    Dim recipe As String = OpcFile.ReadAllText(client, "ns=2;s=Machine/Recipe")
Catch ex As InvalidOperationException
    'The node does not exist or is not a file node.
Catch ex As OpcException
    'The server rejected a file method, e.g. with BadUserAccessDenied.
    Console.WriteLine("{0}: {1}", ex.Code, ex.Message)
End Try

Sicherheit ​

Behandeln Sie Inhalte, die Sie von einem Server lesen, wie Eingaben von außen. Verwenden Sie zum Beispiel Dateinamen oder Pfade aus einer gelesenen Datei nicht ungeprüft für Zugriffe auf das lokale Dateisystem. Begrenzen Sie die Größe (Eigenschaft Length), bevor Sie eine Datei vollständig in den Speicher lesen. Prüfen Sie vor dem Schreiben die Eigenschaft CanUserWrite.

Abgrenzung ​

  • Große Binärwerte, die ein Server nicht als Datei, sondern als Wert einer Variablen bereitstellt, liest die Klasse OpcNodeStream (siehe Große Binärwerte als Stream).
  • Wie ein Server selbst Datei-Nodes bereitstellt, beschreibt die Seite Node-Typen im Server-Kapitel.