Anleitung
Historische Daten
Historische Werte eines Nodes über Zeiträume, zu Zeitpunkten und aggregiert lesen, ändern und löschen sowie die Ereignis-Historie lesen.
Anleitung
Historische Daten
Beteiligte TypenOpcClientOpcNodeIdIOpcNodeHistoryNavigatorOpcHistoryValueOpcModifiedHistoryValueOpcAggregateType
Laut OPC-UA-Spezifikation kann jeder Node der Kategorie Variable die Werte seines Value-Attributs im zeitlichen Verlauf aufzeichnen. Bei jeder Wertänderung des Value-Attributs speichert der Server dann den neuen Wert zusammen mit dem Zeitstempel (engl. Timestamp) des Value-Attributs. Diese Paare aus Wert und Zeitstempel heißen historische Daten. Wo der Server die Daten speichert, entscheidet der Server selbst. Der Client stellt über das Attribut IsHistorizing des Nodes fest, ob der Server für den Node historische Daten bereitstellt beziehungsweise Wertänderungen historisch speichert. Über die NodeId (OpcNodeId) wählt der Client den Node, auf dessen historische Daten er zugreift. Der Client kann historische Daten lesen (engl. read), ändern (engl. update), ersetzen (engl. replace), löschen (engl. delete) oder erzeugen (engl. create). Am häufigsten liest der Client historische Daten. Ob und welche dieser Operationen ein Server für einen Node erlaubt, legt der Server fest. Der Client muss nicht alle historischen Werte verarbeiten, unabhängig davon, ob er mit oder ohne Navigator liest.
Werte eines Zeitfensters lesen
Der Client liest historische Daten über ein Zeitfenster.
Alle Werte innerhalb eines offenen Zeitfensters lesen, bei dem StartTime oder EndTime undefiniert ist:
Alle Werte ab einem bestimmten Zeitstempel (StartTime) lesen:
var startTime = new DateTime(2017, 2, 16, 10, 0, 0);
var history = client.ReadNodeHistory(
startTime, endTime: null, "ns=2;s=Machine/Job/Speed");Dim startTime = New DateTime(2017, 2, 16, 10, 0, 0)
Dim history = client.ReadNodeHistory( _
startTime, endTime:=Nothing, "ns=2;s=Machine/Job/Speed")Alle Werte bis zu einem bestimmten Zeitstempel (EndTime) lesen:
var endTime = new DateTime(2017, 2, 16, 15, 0, 0);
var history = client.ReadNodeHistory(
null, endTime, "ns=2;s=Machine/Job/Speed");Dim endTime = New DateTime(2017, 2, 16, 15, 0, 0)
Dim history = client.ReadNodeHistory( _
Nothing, endTime, "ns=2;s=Machine/Job/Speed")Alle Werte innerhalb eines geschlossenen Zeitfensters lesen, bei dem StartTime und EndTime definiert sind:
var startTime = new DateTime(2017, 2, 16, 10, 0, 0);
var endTime = new DateTime(2017, 2, 16, 15, 0, 0);
var history = client.ReadNodeHistory(
startTime, endTime, "ns=2;s=Machine/Job/Speed");Dim startTime = New DateTime(2017, 2, 16, 10, 0, 0)
Dim endTime = New DateTime(2017, 2, 16, 15, 0, 0)
Dim history = client.ReadNodeHistory( _
startTime, endTime, "ns=2;s=Machine/Job/Speed")Die Werte verarbeiten (über eine Instanz, die die Schnittstelle IEnumerable implementiert):
foreach (var value in history) {
Console.WriteLine(
"{0}: {1}",
value.Timestamp,
value);
}For Each value In history
Console.WriteLine( _
"{0}: {1}",
value.Timestamp,
value)
NextWerte seitenweise lesen
Beim seitenweisen Lesen der historischen Daten ruft der Client immer nur eine bestimmte Anzahl von Werten vom Server ab.
Eine bestimmte Anzahl von Werten je Seite lesen:
var historyNavigator = client.ReadNodeHistory(
10, "ns=2;s=Machine/Job/Speed");Dim historyNavigator = client.ReadNodeHistory( _
10, "ns=2;s=Machine/Job/Speed")Eine bestimmte Anzahl von Werten je Seite innerhalb eines offenen Zeitfensters lesen, bei dem StartTime oder EndTime undefiniert ist:
Eine bestimmte Anzahl von Werten je Seite ab einem bestimmten Zeitstempel (StartTime) lesen:
var startTime = new DateTime(2017, 2, 16, 15, 0, 0);
var historyNavigator = client.ReadNodeHistory(
startTime, 10, "ns=2;s=Machine/Job/Speed");Dim startTime = New DateTime(2017, 2, 16, 15, 0, 0)
Dim historyNavigator = client.ReadNodeHistory( _
startTime, 10, "ns=2;s=Machine/Job/Speed")Eine bestimmte Anzahl von Werten je Seite bis zu einem bestimmten Zeitstempel (EndTime) lesen:
var endTime = new DateTime(2017, 2, 16, 15, 0, 0);
var historyNavigator = client.ReadNodeHistory(
null, endTime, 10, "ns=2;s=Machine/Job/Speed");Dim endTime = New DateTime(2017, 2, 16, 15, 0, 0)
Dim historyNavigator = client.ReadNodeHistory( _
Nothing, endTime, 10, "ns=2;s=Machine/Job/Speed")Eine bestimmte Anzahl von Werten je Seite innerhalb eines geschlossenen Zeitfensters lesen:
var startTime = new DateTime(2017, 2, 16, 10, 0, 0);
var endTime = new DateTime(2017, 2, 16, 15, 0, 0);
var historyNavigator = client.ReadNodeHistory(
startTime, endTime, 10, "ns=2;s=Machine/Job/Speed");Dim startTime = New DateTime(2017, 2, 16, 10, 0, 0)
Dim endTime = New DateTime(2017, 2, 16, 15, 0, 0)
Dim historyNavigator = client.ReadNodeHistory( _
startTime, endTime, 10, "ns=2;s=Machine/Job/Speed")Die Werte verarbeiten (über eine Instanz, die die Schnittstelle IOpcNodeHistoryNavigator implementiert):
do {
foreach (var value in historyNavigator) {
Console.WriteLine(
"{0}: {1}",
value.Timestamp,
value);
}
} while (historyNavigator.MoveNextPage());
historyNavigator.Close();Do
For Each value In historyNavigator
Console.WriteLine( _
"{0}: {1}", _
value.Timestamp, _
value)
Next
Loop While historyNavigator.MoveNextPage()
historyNavigator.Close()Stellen Sie immer sicher, dass die Methode Close des Navigators aufgerufen wird. Nur dann kann der Server die historischen Daten verwerfen, die er für diese Anfrage gepuffert hat. Statt Close explizit aufzurufen, können Sie den Navigator auch in einem using-Block verwenden:
using (historyNavigator) {
do {
foreach (var value in historyNavigator) {
Console.WriteLine(
"{0}: {1}",
value.Timestamp,
value);
}
} while (historyNavigator.MoveNextPage());
}Using historyNavigator
Do
For Each value In historyNavigator
Console.WriteLine( _
"{0}: {1}", _
value.Timestamp, _
value)
Next
Loop While historyNavigator.MoveNextPage()
End UsingDer Navigator fordert eine Seite mit höchstens 999 Anfragen an. Wenn der Server die Seite bis dahin nicht vollständig geliefert hat, löst der Navigator eine OpcException mit dem Status BadCommunicationError aus. Das gilt auch für den Navigator der Ereignis-Historie.
Historie mehrerer Nodes lesen
Beteiligte TypenOpcClientOpcNodeIdIOpcNodeHistoryNavigatorOpcHistoryValue
Die historischen Werte mehrerer Nodes für dasselbe Zeitfenster liest ReadNodesHistory in einem Aufruf. Wenn Sie eine Anzahl von Werten je Seite angeben, liefert die Methode einen Navigator. Sein Indexer liefert die Werte eines einzelnen Nodes. Weitere Seiten lädt der Client wie beim einzelnen Node nach:
var nodeIds = new OpcNodeId[] { "ns=2;s=Machine/Speed", "ns=2;s=Machine/Temperature" };
using (var history = client.ReadNodesHistory(
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
numberOfValues: 100,
nodeIds)) {
foreach (OpcHistoryValue value in history[nodeIds[0]])
Console.WriteLine("{0}: {1}", value.Timestamp, value.Value);
}Dim nodeIds = New OpcNodeId() {"ns=2;s=Machine/Speed", "ns=2;s=Machine/Temperature"}
Using history = client.ReadNodesHistory( _
DateTime.UtcNow.AddHours(-1), _
DateTime.UtcNow, _
numberOfValues:=100, _
nodeIds)
For Each value As OpcHistoryValue In history(nodeIds(0))
Console.WriteLine("{0}: {1}", value.Timestamp, value.Value)
Next
End UsingOhne Anzahl je Seite liefert ReadNodesHistory alle Werte des Zeitfensters als Wörterbuch mit dem Identifier des Nodes als Schlüssel:
IDictionary<OpcNodeId, IEnumerable<OpcHistoryValue>> history = client.ReadNodesHistory(
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
"ns=2;s=Machine/Speed",
"ns=2;s=Machine/Temperature");
foreach (var entry in history)
Console.WriteLine("{0}: {1} values", entry.Key, entry.Value.Count());Dim history As IDictionary(Of OpcNodeId, IEnumerable(Of OpcHistoryValue)) = _
client.ReadNodesHistory( _
DateTime.UtcNow.AddHours(-1), _
DateTime.UtcNow, _
"ns=2;s=Machine/Speed", _
"ns=2;s=Machine/Temperature")
For Each entry In history
Console.WriteLine("{0}: {1} values", entry.Key, entry.Value.Count())
NextAggregierte Werte lesen
Für das verarbeitete Lesen (engl. processed) der historischen Daten wählen Sie über OpcAggregateType eine von mehreren Arten der Aggregation.
Den kleinsten Wert innerhalb eines Zeitfensters lesen:
var minSpeed = client.ReadNodeHistoryProcessed(
startTime,
endTime,
OpcAggregateType.Minimum,
"ns=2;s=Machine/Job/Speed");Dim minSpeed = client.ReadNodeHistoryProcessed( _
startTime, _
endTime, _
OpcAggregateType.Minimum, _
"ns=2;s=Machine/Job/Speed")Den durchschnittlichen Wert innerhalb eines Zeitfensters lesen:
var avgSpeed = client.ReadNodeHistoryProcessed(
startTime,
endTime,
OpcAggregateType.Average,
"ns=2;s=Machine/Job/Speed");Dim avgSpeed = client.ReadNodeHistoryProcessed( _
startTime, _
endTime, _
OpcAggregateType.Average, _
"ns=2;s=Machine/Job/Speed")Den größten Wert innerhalb eines Zeitfensters lesen:
var maxSpeed = client.ReadNodeHistoryProcessed(
startTime,
endTime,
OpcAggregateType.Maximum,
"ns=2;s=Machine/Job/Speed");Dim maxSpeed = client.ReadNodeHistoryProcessed( _
startTime, _
endTime, _
OpcAggregateType.Maximum, _
"ns=2;s=Machine/Job/Speed")Wenn Sie die Werte in Abschnitte gleicher Länge aufteilen, liefert der Server je Abschnitt einen aggregierten Wert. Die Länge der Abschnitte geben Sie in Millisekunden an. Ohne Angabe berechnet der Server einen Wert über das gesamte Zeitfenster:
Den durchschnittlichen Wert je Stunde innerhalb eines Zeitfensters lesen:
// Read the average value of each hour within the time window.
var avgSpeedPerHour = client.ReadNodeHistoryProcessed(
startTime,
endTime,
OpcAggregateType.Average,
aggregateInterval: TimeSpan.FromHours(1).TotalMilliseconds,
"ns=2;s=Machine/Job/Speed");'Read the average value of each hour within the time window.
Dim avgSpeedPerHour = client.ReadNodeHistoryProcessed( _
startTime, _
endTime, _
OpcAggregateType.Average, _
aggregateInterval:=TimeSpan.FromHours(1).TotalMilliseconds, _
"ns=2;s=Machine/Job/Speed")Aggregierte Werte tragen nur den Zeitstempel der Quelle (SourceTimestamp), keinen Zeitstempel des Servers.
Werte zu bestimmten Zeitpunkten lesen
Wenn der Client Werte zu bestimmten Zeitpunkten liest, liefert der Server je Zeitpunkt einen Wert. Wenn zu einem Zeitpunkt kein aufgezeichneter Wert vorliegt, ermittelt der Server den Wert in der Regel anhand der benachbarten Werte:
var times = new DateTime[] {
new DateTime(2017, 2, 16, 10, 0, 0),
new DateTime(2017, 2, 16, 12, 0, 0)
};
var speeds = client.ReadNodeHistoryAtTime(times, "ns=2;s=Machine/Job/Speed");
foreach (var value in speeds)
Console.WriteLine("{0}: {1}", value.Timestamp, value);Dim times = New DateTime() { _
New DateTime(2017, 2, 16, 10, 0, 0), _
New DateTime(2017, 2, 16, 12, 0, 0) _
}
Dim speeds = client.ReadNodeHistoryAtTime(times, "ns=2;s=Machine/Job/Speed")
For Each value In speeds
Console.WriteLine("{0}: {1}", value.Timestamp, value)
NextGeänderte Werte lesen
Wenn ein Client historische Werte geändert, ersetzt oder gelöscht hat (Abschnitt Historische Daten ändern), kann ein Server die vorherigen Werte zusammen mit Informationen über die Änderung aufbewahren. Diese Werte liefert die Methode ReadNodeHistoryModified als Instanzen von OpcModifiedHistoryValue. Wie bei den übrigen Methoden kann der Client die Werte auch seitenweise lesen:
var modifiedSpeeds = client.ReadNodeHistoryModified(
startTime,
endTime,
"ns=2;s=Machine/Job/Speed");
foreach (var value in modifiedSpeeds) {
Console.WriteLine(
"{0}: {1} ({2} at {3} by {4})",
value.Timestamp,
value,
value.ModificationType,
value.ModificationTime,
value.ModificationUserName);
}Dim modifiedSpeeds = client.ReadNodeHistoryModified( _
startTime, _
endTime, _
"ns=2;s=Machine/Job/Speed")
For Each value In modifiedSpeeds
Console.WriteLine( _
"{0}: {1} ({2} at {3} by {4})", _
value.Timestamp, _
value, _
value.ModificationType, _
value.ModificationTime, _
value.ModificationUserName)
NextGegenüber einem Server mit OpcNodeHistorian beziehen sich ReadNodeHistoryModified und DeleteNodeHistoryModified auf die Zeitstempel der Werte, nicht auf den Zeitpunkt der Änderung.
Wenn der Server die Anfrage für den Node ablehnt, lösen ReadNodeHistoryProcessed, ReadNodeHistoryAtTime und ReadNodeHistoryModified eine OpcException mit dem Status des Servers aus, zum Beispiel BadAggregateNotSupported. Wenn der Server den Node annimmt, aber keine Werte liefert, ist das Ergebnis leer.
Historische Daten ändern
Beteiligte TypenOpcClientOpcNodeIdOpcValueOpcStatusOpcStatusCollection
Um historische Daten zu ändern, übergibt der Client die Werte als Instanzen von OpcValue. Deren SourceTimestamp legt den Zeitpunkt des Werts fest. Je Wert liefert der Server einen OpcStatus. In OPC UA gelten dabei die folgenden Regeln:
CreateNodeHistoryfügt Werte nur zu Zeitpunkten hinzu, zu denen noch kein Wert existiert.ReplaceNodeHistoryersetzt nur bereits existierende Werte.UpdateNodeHistoryfügt Werte hinzu oder ersetzt bereits existierende Werte.
Neue Werte hinzufügen:
OpcStatusCollection results = client.CreateNodeHistory(
"ns=2;s=Machine/Job/Speed",
new OpcValue(1200, sourceTimestamp: new DateTime(2017, 2, 16, 10, 0, 0)),
new OpcValue(1300, sourceTimestamp: new DateTime(2017, 2, 16, 10, 5, 0)));
foreach (var result in results) {
if (result.IsBad)
Console.WriteLine("Failed to create value: {0}", result.Description);
}Dim results As OpcStatusCollection = client.CreateNodeHistory( _
"ns=2;s=Machine/Job/Speed", _
New OpcValue(1200, sourceTimestamp:=New DateTime(2017, 2, 16, 10, 0, 0)), _
New OpcValue(1300, sourceTimestamp:=New DateTime(2017, 2, 16, 10, 5, 0)))
For Each result In results
If result.IsBad Then
Console.WriteLine("Failed to create value: {0}", result.Description)
End If
NextEinen existierenden Wert ersetzen:
OpcStatus result = client.ReplaceNodeHistory(
"ns=2;s=Machine/Job/Speed",
new OpcValue(1250, sourceTimestamp: new DateTime(2017, 2, 16, 10, 0, 0)));Dim result As OpcStatus = client.ReplaceNodeHistory( _
"ns=2;s=Machine/Job/Speed", _
New OpcValue(1250, sourceTimestamp:=New DateTime(2017, 2, 16, 10, 0, 0)))Einen Wert hinzufügen oder ersetzen:
OpcStatus result = client.UpdateNodeHistory(
"ns=2;s=Machine/Job/Speed",
new OpcValue(1250, sourceTimestamp: new DateTime(2017, 2, 16, 10, 0, 0)));Dim result As OpcStatus = client.UpdateNodeHistory( _
"ns=2;s=Machine/Job/Speed", _
New OpcValue(1250, sourceTimestamp:=New DateTime(2017, 2, 16, 10, 0, 0)))Werte eines Zeitfensters oder zu bestimmten Zeitpunkten löschen:
// Delete all values within the time window.
OpcStatusCollection results = client.DeleteNodeHistory(
"ns=2;s=Machine/Job/Speed",
startTime,
endTime);
// Delete the value of the timestamp specified.
OpcStatus result = client.DeleteNodeHistoryAtTime(
"ns=2;s=Machine/Job/Speed",
time: new DateTime(2017, 2, 16, 10, 5, 0));'Delete all values within the time window.
Dim results As OpcStatusCollection = client.DeleteNodeHistory( _
"ns=2;s=Machine/Job/Speed", _
startTime, _
endTime)
'Delete the value of the timestamp specified.
Dim result As OpcStatus = client.DeleteNodeHistoryAtTime( _
"ns=2;s=Machine/Job/Speed", _
time:=New DateTime(2017, 2, 16, 10, 5, 0))Ereignis-Historie lesen
Beteiligte TypenOpcClientOpcNodeIdOpcEventFilterOpcEventIOpcEventHistoryNavigator
Neben den Werten von Variablen kann ein Server auch vergangene Ereignisse aufzeichnen. Der Client liest die Ereignis-Historie über den Node, der die Ereignisse meldet, zum Beispiel den Server-Node oder einen Ereignis-Node (Seite Ereignisse). Welche Ereignisse und welche Felder der Server liefert, legt ein Ereignisfilter fest. Sie erstellen ihn wie beim Abonnieren von Ereignissen (Abschnitt Ereignisse filtern):
var severity = new OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity");
var filter = OpcFilter.Using(client)
.FromEvents(OpcEventTypes.AlarmCondition)
.Where(severity > OpcEventSeverity.Medium)
.Select();
IEnumerable<OpcEvent> events = client.ReadEventHistory(
startTime,
endTime,
"ns=2;s=Machine",
filter);
foreach (var historicalEvent in events)
Console.WriteLine("{0}: {1}", historicalEvent.Time, historicalEvent.Message);Dim severity = New OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity")
Dim filter = OpcFilter.Using(client) _
.FromEvents(OpcEventTypes.AlarmCondition) _
.Where(severity > OpcEventSeverity.Medium) _
.Select()
Dim events As IEnumerable(Of OpcEvent) = client.ReadEventHistory( _
startTime, _
endTime, _
"ns=2;s=Machine", _
filter)
For Each historicalEvent In events
Console.WriteLine("{0}: {1}", historicalEvent.Time, historicalEvent.Message)
NextDie Ereignisse seitenweise lesen: Wie bei den historischen Werten liefert ein Navigator die Ereignisse seitenweise. Stellen Sie auch hier sicher, dass der Navigator geschlossen wird, zum Beispiel über einen using-Block:
using (var eventNavigator = client.ReadEventHistory(
startTime,
endTime,
numberOfEvents: 10,
"ns=2;s=Machine",
filter)) {
do {
foreach (var historicalEvent in eventNavigator)
Console.WriteLine("{0}: {1}", historicalEvent.Time, historicalEvent.Message);
} while (eventNavigator.MoveNextPage());
}Using eventNavigator = client.ReadEventHistory( _
startTime, _
endTime, _
numberOfEvents:=10, _
"ns=2;s=Machine", _
filter)
Do
For Each historicalEvent In eventNavigator
Console.WriteLine("{0}: {1}", historicalEvent.Time, historicalEvent.Message)
Next
Loop While eventNavigator.MoveNextPage()
End UsingEin Server ist nicht verpflichtet, vergangene Werte oder Ereignisse bereitzustellen.
Historische Daten eines OPC HDA Servers
Die historischen Daten eines OPC HDA Servers (Historical Data Access) liest der Client mit denselben Methoden. Den Ablauf beschreibt der Abschnitt Historische Daten (HDA) auf der Seite OPC Classic.

