/* ======================================================================== * Copyright (c) 2005-2016 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using Opc.Ua.Client; using Opc.Ua.Configuration; using System; using System.Collections.Generic; namespace Opc.Ua.Sample { partial class Program { public const double PublishingInterval = 1; public const double SamplingInterval = 1; public const uint QueueSize = 3; public const int ItemsToMonitor = 100000; public const int ItemsPerPublish = 10000; public const uint NotificationsPerPublish = ItemsPerPublish * QueueSize; /// /// The URL of the server. /// // public const string DefaultServerUrl = "opc.tcp://localhost:21381/UA/MatrikonOpcUaWrapper"; public const string DefaultServerUrl = "opc.tcp://localhost:51210/UA/SampleServer"; /// /// The variables to read. /// static List VariableBrowsePaths; static void Main(string[] args) { VariableBrowsePaths = new List(); VariableBrowsePaths.Add("/6:Data/6:Dynamic/6:Scalar/6:Int32Value"); // VariableBrowsePaths.Add("/7:MatrikonOpc Sim Server/7:Simulation Items/7:Bucket Brigade/7:Int1"); // VariableBrowsePaths.Add("/7:MatrikonOPC Sim Server/7:Simulation Items/7:Bucket Brigade/7:Int2"); ApplicationInstance application = new ApplicationInstance(); application.ApplicationName = "UA History Client"; application.ApplicationType = ApplicationType.Client; application.ConfigSectionName = "Opc.Ua.SampleClient"; try { application.LoadApplicationConfiguration(false).Wait(); // check the application certificate. bool certOK = application.CheckApplicationInstanceCertificate(false, 0).Result; if (!certOK) { throw new Exception("Application instance certificate invalid!"); } application.ApplicationConfiguration.CertificateValidator.CertificateValidation += new CertificateValidationEventHandler(CertificateValidator_CertificateValidation); // get list of cached endpoints. ConfiguredEndpointCollection endpoints = application.ApplicationConfiguration.LoadCachedEndpoints(true); endpoints.DiscoveryUrls = application.ApplicationConfiguration.ClientConfiguration.WellKnownDiscoveryUrls; // Wrap the channel with the session object. // This call will fail if the server does not trust the client certificate. Session session = Session.Create(application.ApplicationConfiguration, endpoints[1], true, "MySession", 60000, null, null).Result; session.ReturnDiagnostics = DiagnosticsMasks.All; //Read some history values: string str = ""; do { Console.WriteLine("Select action from the menu:\n"); Console.WriteLine("\t 0 - Browse"); Console.WriteLine("\t 1 - Update"); Console.WriteLine("\t 2 - ReadRaw"); Console.WriteLine("\t 3 - ReadProcessed"); Console.WriteLine("\t 4 - ReadAtTime"); Console.WriteLine("\t 5 - ReadAttributes"); Console.WriteLine("\t 6 - DeleteAtTime"); Console.WriteLine("\t 7 - DeleteRaw"); Console.WriteLine("\n\tQ - exit\n\n"); str = Console.ReadLine(); Console.WriteLine("\n"); try { if (str == "0") { Browse(session); } else if (str == "1") HistoryUpdate(session); else if (str == "2") HistoryReadRaw(session); else if (str == "3") HistoryReadProcessed(session); else if (str == "4") HistoryReadAtTime(session); else if (str == "5") HistoryReadAttributes(session); else if (str == "6") HistoryDeleteAtTime(session); else if (str == "7") HistoryDeleteRaw(session); } catch (Exception e) { Console.WriteLine("Exception occured: " + e.Message); } } while (str != "Q" && str != "q"); // Display some friendly info to the console and then wait for the ENTER key to be pressed. Console.WriteLine("Connected to {0}.\nPress ENTER to disconnect to end.", DefaultServerUrl); Console.ReadLine(); // Close and Dispose of our session, effectively disconnecting us from the UA Server. session.Close(); session.Dispose(); } catch (Exception e) { Console.WriteLine("Unexpected exception: {0}.\nPress ENTER to disconnect to end.", e.Message); Console.ReadLine(); Console.WriteLine(); Console.WriteLine("========================================================================================"); Console.WriteLine(); } } private class NodeOfInterest { public NodeId NodeId; public DataValue Value; } /// /// Returns the node ids for a set of relative paths. /// /// An open session with the server to use. /// The starting node for the relative paths. /// The relative paths. /// A collection of local nodes. static List GetNodeIds( Session session, NodeId startNodeId, params string[] relativePaths) { // build the list of browse paths to follow by parsing the relative paths. BrowsePathCollection browsePaths = new BrowsePathCollection(); if (relativePaths != null) { for (int ii = 0; ii < relativePaths.Length; ii++) { BrowsePath browsePath = new BrowsePath(); // The relative paths used indexes in the namespacesUris table. These must be // converted to indexes used by the server. An error occurs if the relative path // refers to a namespaceUri that the server does not recognize. // The relative paths may refer to ReferenceType by their BrowseName. The TypeTree object // allows the parser to look up the server's NodeId for the ReferenceType. browsePath.RelativePath = RelativePath.Parse( relativePaths[ii], session.TypeTree, session.NamespaceUris, session.NamespaceUris); browsePath.StartingNode = startNodeId; browsePaths.Add(browsePath); } } // make the call to the server. BrowsePathResultCollection results; DiagnosticInfoCollection diagnosticInfos; ResponseHeader responseHeader = session.TranslateBrowsePathsToNodeIds( null, browsePaths, out results, out diagnosticInfos); // ensure that the server returned valid results. Session.ValidateResponse(results, browsePaths); Session.ValidateDiagnosticInfos(diagnosticInfos, browsePaths); Console.WriteLine("Translated {0} browse paths.", relativePaths.Length); // collect the list of node ids found. List nodes = new List(); for (int ii = 0; ii < results.Count; ii++) { // check if the start node actually exists. if (StatusCode.IsBad(results[ii].StatusCode)) { ServiceResult error = new ServiceResult( results[ii].StatusCode, diagnosticInfos.Count > ii ? diagnosticInfos[ii] : null, responseHeader.StringTable); Console.WriteLine("Path '{0}' is not valid. Error = {1}", relativePaths[ii], error); continue; } // an empty list is returned if no node was found. if (results[ii].Targets.Count == 0) { Console.WriteLine("Path '{0}' does not exist.", relativePaths[ii]); continue; } // Multiple matches are possible, however, the node that matches the type model is the // one we are interested in here. The rest can be ignored. BrowsePathTarget target = results[ii].Targets[0]; if (target.RemainingPathIndex != UInt32.MaxValue) { Console.WriteLine("Path '{0}' refers to a node in another server.", relativePaths[ii]); continue; } // The targetId is an ExpandedNodeId because it could be node in another server. // The ToNodeId function is used to convert a local NodeId stored in a ExpandedNodeId to a NodeId. NodeOfInterest node = new NodeOfInterest(); node.NodeId = ExpandedNodeId.ToNodeId(target.TargetId, session.NamespaceUris); nodes.Add(node); } Console.WriteLine("Translate found {0} local nodes.", nodes.Count); // return whatever was found. return nodes; } /// /// Used to synchronize access to data via multiple threads. /// private static object m_lock = new object(); /// /// Handles an error validating the server certificate. /// /// /// Applications should never accept certificates silently. Doing do will create the illusion of security /// that will come back to haunt the vendor in the future. Compliance tests by the OPC Foundation will /// fail products that silently accept untrusted certificates. /// static void CertificateValidator_CertificateValidation(CertificateValidator validator, CertificateValidationEventArgs e) { e.Accept = true; Console.WriteLine("WARNING: Accepting Untrusted Certificate: {0}", e.Certificate.Subject); } private static Queue m_publishes = new Queue(); private static DateTime m_lastDump; private static uint m_lastMessage; private static uint m_actualSamples; private static uint m_expectedSamples; private static uint m_dumpCount; private static uint m_notifications; private static void Save(uint id, DataValue value) { lock (m_publishes) { m_publishes.Enqueue(value); } } /// /// Raised when a publish response arrives from the server. /// static void Session_Notification(Session session, NotificationEventArgs e) { NotificationMessage message = e.NotificationMessage; // check for keep alive. if (message.NotificationData.Count == 0) { Console.WriteLine( "===>>> Subscription KeepAlive: SubscriptionId={0} MessageId={1} Time={2:HH:mm:ss.fff}", e.Subscription.Id, message.SequenceNumber, message.PublishTime.ToLocalTime()); return; } DataChangeNotification dcn = (DataChangeNotification)ExtensionObject.ToEncodeable(message.NotificationData[0]); // Console.WriteLine("{0:mm:ss.fff} - SeqNo={1}, Items={2}", message.PublishTime, message.SequenceNumber, dcn.MonitoredItems.Count); int count = 0; // get the data changes (oldest to newest). foreach (MonitoredItemNotification datachange in message.GetDataChanges(false)) { // lookup the monitored item. MonitoredItem monitoredItem = e.Subscription.FindItemByClientHandle(datachange.ClientHandle); if (monitoredItem == null) { Console.WriteLine("MonitoredItem ClientHandle not known: {0}", datachange.ClientHandle); continue; } // this is called on another thread so we need to synchronize before accessing the node. lock (m_lock) { NodeOfInterest node = monitoredItem.Handle as NodeOfInterest; //Console.WriteLine( // "Update for {0}: {1} Status={2} Timestamp={3:HH:mm:ss.fff}", // node.DisplayName, // datachange.Value.WrappedValue, // datachange.Value.StatusCode, // datachange.Value.SourceTimestamp.ToLocalTime()); node.Value = datachange.Value; Save(datachange.ClientHandle, node.Value); count++; } } if (count > NotificationsPerPublish) { Console.WriteLine("Too many notifications in Publish: {0}/{1}", count, NotificationsPerPublish); } lock (m_publishes) { m_notifications++; if (m_lastDump.AddSeconds(1) > DateTime.UtcNow) { return; } int sampleCount = 0; int itemCount = 0; DateTime timestamp = DateTime.MinValue; while (m_publishes.Count > 0) { DataValue value1 = m_publishes.Dequeue(); if (timestamp < value1.SourceTimestamp) { if (timestamp != DateTime.MinValue) { //Console.WriteLine( // "Items = {1}, Timestamp = {0:mm:ss.fff}", // timestamp, // itemCount); } timestamp = value1.SourceTimestamp; itemCount = 0; } sampleCount++; itemCount++; } //Console.WriteLine( // "Items = {1}, Timestamp = {0:mm:ss.fff}", // timestamp, // itemCount); uint expectedSamples = 10000; // (uint)((1000.0/SamplingInterval)*ItemsToMonitor); uint expectedNotifications = 50; Console.WriteLine( "{0:mm:ss.fff}-{1:mm:ss.fff}, Messages = {2}/{3}, Samples = {4}/{5}, MissedSamples = {6}", m_lastDump, m_lastDump.AddSeconds(1), m_notifications, expectedNotifications, sampleCount, expectedSamples, (int)m_expectedSamples - (int)m_actualSamples); m_lastDump = m_lastDump.AddSeconds(1); m_lastMessage = message.SequenceNumber; m_dumpCount++; m_notifications = 0; m_actualSamples += (uint)sampleCount; m_expectedSamples += expectedSamples; if (m_dumpCount == 10) { m_actualSamples = 0; m_expectedSamples = 0; } } } /// /// Reads the values for a set of variables. /// static void Read(Session session) { IList results = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); // build list of nodes to read. ReadValueIdCollection nodesToRead = new ReadValueIdCollection(); for (int ii = 0; ii < results.Count; ii++) { ReadValueId nodeToRead = new ReadValueId(); nodeToRead.NodeId = results[ii].NodeId; nodeToRead.AttributeId = Attributes.Value; nodesToRead.Add(nodeToRead); } // read values. DataValueCollection values; DiagnosticInfoCollection diagnosticInfos; ResponseHeader responseHeader = session.Read( null, 0, TimestampsToReturn.Both, nodesToRead, out values, out diagnosticInfos); // verify that the server returned the correct number of results. Session.ValidateResponse(values, nodesToRead); Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead); // process results. for (int ii = 0; ii < values.Count; ii++) { // check for error. if (StatusCode.IsBad(values[ii].StatusCode)) { ServiceResult result = Session.GetResult(values[ii].StatusCode, ii, diagnosticInfos, responseHeader); Console.WriteLine("Read result for {0}: {1}", VariableBrowsePaths[ii], result.ToLongString()); continue; } // write value. Console.WriteLine( "{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}",nodesToRead[ii].NodeId, values[ii].Value.ToString(), values[ii].StatusCode.ToString(), values[ii].ServerTimestamp, values[ii].SourceTimestamp); } } /// /// Reads the history of values for a set of variables. /// static void HistoryReadRaw(Session session) { // translate browse paths. IList nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; ReadRawModifiedDetails readDetails = new ReadRawModifiedDetails(); readDetails.StartTime = DateTime.MinValue; readDetails.EndTime = DateTime.Now; readDetails.IsReadModified = false; readDetails.NumValuesPerNode = 100; readDetails.ReturnBounds = false; ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = false; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; HistoryData historyData = null; DataValueCollection dataValues = null; if (historyReadResult.HistoryData != null) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; dataValues = historyData.DataValues; } ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); Console.WriteLine("HistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (StatusCode.IsBad(historyReadResult.StatusCode)) { continue; } if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; // write value. Console.WriteLine("{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}", jj, dataValue.Value == null ? "null" : dataValue.Value.ToString(), dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } } /// /// Reads the history of attributes for Bucket Brigade.Int1. /// static void HistoryReadAttributes(Session session) { List VariableBrowsePaths = new List(); VariableBrowsePaths.Add("/7:MatrikonOpc Sim Server/7:Simulation Items/7:Bucket Brigade/7:Int1/7:Description"); VariableBrowsePaths.Add("/7:MatrikonOpc Sim Server/7:Simulation Items/7:Bucket Brigade/7:Int1/7:DataType"); VariableBrowsePaths.Add("/7:MatrikonOpc Sim Server/7:Simulation Items/7:Bucket Brigade/7:Int1/7:ITEMID"); // translate browse paths. IList nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; ReadRawModifiedDetails readDetails = new ReadRawModifiedDetails(); readDetails.StartTime = DateTime.MinValue; readDetails.EndTime = DateTime.Now; readDetails.IsReadModified = false; readDetails.NumValuesPerNode = 100; readDetails.ReturnBounds = false; ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = false; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; HistoryData historyData = null; DataValueCollection dataValues = null; if (historyReadResult.HistoryData != null) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; dataValues = historyData.DataValues; } ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); Console.WriteLine("\nHistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (StatusCode.IsBad(historyReadResult.StatusCode)) { continue; } if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; // write value. Console.WriteLine("\t{0}: V={1}",jj, dataValue.Value == null ? "null" : dataValue.Value.ToString()); Console.WriteLine("\t Q={0}, SrvT={1}, SrcT={2}\n", dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } } /// /// Reads the history of values for a set of variables at given time. /// static void HistoryReadAtTime(Session session) { // translate browse paths. IList nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; ReadAtTimeDetails readDetails = new ReadAtTimeDetails(); readDetails.ReqTimes = new DateTimeCollection(); for (int jj = 0; jj < 10; jj++) { readDetails.ReqTimes.Add(new DateTime(2008, 01, 01, 12, 0, jj)); readDetails.ReqTimes.Add(new DateTime(2008, 01, 01, 12, 0, jj, (int) 500)); } ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = false; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; HistoryData historyData = null; DataValueCollection dataValues = null; if (historyReadResult.HistoryData != null) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; dataValues = historyData.DataValues; } ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); Console.WriteLine("HistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (StatusCode.IsBad(historyReadResult.StatusCode)) { continue; } if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; // write value. Console.WriteLine("{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}", jj, dataValue.Value == null ? "null" : dataValue.Value.ToString(), dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } } /// /// Deletes the history of values for a set of variables at given time. /// static void HistoryDeleteAtTime(Session session) { // translate browse paths. IList results = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; ExtensionObjectCollection eoc = new ExtensionObjectCollection(); for (int ii = 0; ii < results.Count; ii++) { DeleteAtTimeDetails deleteDetails = new DeleteAtTimeDetails(); deleteDetails.ReqTimes = new DateTimeCollection(); for (int jj = 0; jj < 5; jj++) { deleteDetails.ReqTimes.Add(new DateTime(2008, 01, 01, 12, 0, jj * 2)); } deleteDetails.NodeId = results[ii].NodeId; deleteDetails.Processed = false; ExtensionObject eo = new ExtensionObject(deleteDetails.TypeId, deleteDetails); eoc.Add(eo); } HistoryUpdateResultCollection historyUpdateResults; ResponseHeader responseHeader = session.HistoryUpdate(null, eoc, out historyUpdateResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyUpdateResults.Count; ii++) { HistoryUpdateResult historyUpdateResult = historyUpdateResults[ii]; Console.WriteLine("HistoryUpdate result code for {0}: {1}", VariableBrowsePaths[ii], historyUpdateResult.StatusCode.ToString()); if (StatusCode.IsGood(historyUpdateResult.StatusCode)) { for (int jj = 0; jj < historyUpdateResult.OperationResults.Count; jj++) { Console.WriteLine(" {0}: {1}", jj, historyUpdateResult.OperationResults[jj]); } Console.WriteLine(""); } } } /// /// Deletes the history of values for a set of variables at given time interval. /// static void HistoryDeleteRaw(Session session) { // translate browse paths. IList results = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; ExtensionObjectCollection eoc = new ExtensionObjectCollection(); for (int ii = 0; ii < results.Count; ii++) { DeleteRawModifiedDetails deleteDetails = new DeleteRawModifiedDetails(); deleteDetails.StartTime = new DateTime(2008, 1, 1, 12, 0, 0); deleteDetails.EndTime = new DateTime(2008, 1, 1, 12, 0, 10); deleteDetails.NodeId = results[ii].NodeId; deleteDetails.IsDeleteModified = false; ExtensionObject eo = new ExtensionObject(deleteDetails.TypeId, deleteDetails); eoc.Add(eo); } HistoryUpdateResultCollection historyUpdateResults; ResponseHeader responseHeader = session.HistoryUpdate(null, eoc, out historyUpdateResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyUpdateResults.Count; ii++) { HistoryUpdateResult historyUpdateResult = historyUpdateResults[ii]; Console.WriteLine("HistoryUpdate result code for {0}: {1}", VariableBrowsePaths[ii], historyUpdateResult.StatusCode.ToString()); if (StatusCode.IsGood(historyUpdateResult.StatusCode)) { for (int jj = 0; jj < historyUpdateResult.OperationResults.Count; jj++) { Console.WriteLine(" {0}: {1}", jj, historyUpdateResult.OperationResults[jj]); } Console.WriteLine(""); } } } static void HistoryReadProcessed(Session session) { // translate browse paths. IList nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); DiagnosticInfoCollection diagnosticInfos; NodeId aggregateNodeId = null; RequestHeader rh = null; ViewDescription vd = null; ReferenceDescriptionCollection references; byte[] cp; //Get the list of avalilable aggregate functions: session.Browse( rh, vd, Opc.Ua.ObjectIds.Server_ServerCapabilities_AggregateFunctions, 1000, BrowseDirection.Forward, ReferenceTypeIds.Aggregates, false, 0, out cp, out references); Console.WriteLine("{0} aggregates are detected:", references.Count); //Print the list of avalible aggregates: int i = 0; foreach (ReferenceDescription rd in references) { i++; Console.WriteLine("{0}. {1} {2}", i, rd.BrowseName, rd.NodeId.Identifier.ToString()); } //Select aggregate function: Console.WriteLine("\nEnter aggregate number: "); string str = Console.ReadLine(); i = System.Int16.Parse(str); if (i > 0 && i <= references.Count) { aggregateNodeId = ExpandedNodeId.ToNodeId(references[i - 1].NodeId, session.NamespaceUris); } //Prepare arguments to pass to read processed history ReadProcessedDetails readDetails = new ReadProcessedDetails(); readDetails.StartTime = new DateTime(2008, 1, 1, 12, 0, 0); readDetails.EndTime = new DateTime(2008, 1, 1, 12, 0, 12); readDetails.AggregateType = new NodeIdCollection(nodeIds.Count); for (int x = 0; x < nodeIds.Count; x++) { readDetails.AggregateType.Add (aggregateNodeId); } readDetails.ProcessingInterval = 500; //500 milliseconds ExtensionObject eo = new ExtensionObject(readDetails.TypeId, readDetails); HistoryReadValueIdCollection idCollection = new HistoryReadValueIdCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { HistoryReadValueId readValueId = new HistoryReadValueId(); readValueId.NodeId = nodeIds[ii].NodeId; readValueId.Processed = true; idCollection.Add(readValueId); } HistoryReadResultCollection historyReadResults; //Read processed history: ResponseHeader responseHeader = session.HistoryRead(null, eo, TimestampsToReturn.Both, true, idCollection, out historyReadResults, out diagnosticInfos); //Print results: for (int ii = 0; ii < historyReadResults.Count; ii++) { HistoryReadResult historyReadResult = historyReadResults[ii]; ServiceResult result = Session.GetResult(historyReadResult.StatusCode, ii, diagnosticInfos, responseHeader); HistoryData historyData = null; DataValueCollection dataValues = null; if ( !(historyReadResult.HistoryData == null) ) { historyData = ExtensionObject.ToEncodeable(historyReadResult.HistoryData) as HistoryData; if (historyData == null) dataValues = null; else dataValues = historyData.DataValues; } Console.WriteLine("\nHistoryRead result code for {0}: {1}", VariableBrowsePaths[ii], result.StatusCode.ToString()); if (dataValues == null) { Console.WriteLine("dataValues == null"); continue; } for (int jj = 0; jj < dataValues.Count; jj++) { DataValue dataValue = dataValues[jj]; if (dataValue == null) continue; // write value. Console.WriteLine("{0}: V={1}, Q={2}, SrvT={3}, SrcT={4}", jj, dataValue.Value == null ? "null" : dataValue.Value.ToString(), dataValue.StatusCode.ToString(), dataValue.ServerTimestamp, dataValue.SourceTimestamp); } } } static void Browse(Session session) { DiagnosticInfoCollection diagnosticInfos; BrowseDescriptionCollection bc = new BrowseDescriptionCollection(); BrowseDescription bd = new BrowseDescription(); bd.BrowseDirection = BrowseDirection.Forward; NodeId nodeId = Opc.Ua.Objects.ObjectsFolder; do { Console.WriteLine("\n Enter nodeId to Browse (or q to exit)"); string s = Console.ReadLine(); if (s == "q") break; if (s.Length == 0) { nodeId = Opc.Ua.Objects.ObjectsFolder; } else nodeId = new NodeId(s); bc.Clear(); bd.NodeId = nodeId; bc.Add(bd); BrowseResultCollection results; ResponseHeader rh = session.Browse(null, null, 100, bc, out results, out diagnosticInfos); foreach ( BrowseResult res in results) { foreach (ReferenceDescription rdc in res.References) { Console.WriteLine(String.Format(" Node = {0} (namespace {1}) {2}", rdc.NodeId.ToString(),rdc.NodeId.NamespaceIndex, Environment.NewLine)); } } } while (true); } static void HistoryUpdate(Session session) { DiagnosticInfoCollection diagnosticInfos; // translate browse paths. IList nodeIds; nodeIds = GetNodeIds(session, Opc.Ua.Objects.ObjectsFolder, VariableBrowsePaths.ToArray()); ExtensionObjectCollection eoc = new ExtensionObjectCollection(); for (int ii = 0; ii < nodeIds.Count; ii++) { UpdateDataDetails updateDetails = new UpdateDataDetails(); updateDetails.NodeId = nodeIds[ii].NodeId; updateDetails.PerformInsertReplace = PerformUpdateType.Update; updateDetails.UpdateValues = new DataValueCollection(); for (int jj = 0; jj <= 5; jj++) { DataValue dv = new DataValue(new Variant(jj*10), StatusCodes.Good, new DateTime(2008, 01, 01, 12, 0, jj*2)); updateDetails.UpdateValues.Add(dv); } ExtensionObject eo = new ExtensionObject(updateDetails.TypeId, updateDetails); eoc.Add(eo); } HistoryUpdateResultCollection historyUpdateResults; ResponseHeader responseHeader = session.HistoryUpdate(null, eoc, out historyUpdateResults, out diagnosticInfos); // process results. for (int ii = 0; ii < historyUpdateResults.Count; ii++) { HistoryUpdateResult historyUpdateResult = historyUpdateResults[ii]; Console.WriteLine("HistoryUpdate result code for {0}: {1}", VariableBrowsePaths[ii], historyUpdateResult.StatusCode.ToString()); if (StatusCode.IsGood(historyUpdateResult.StatusCode)) { for (int jj = 0; jj < historyUpdateResult.OperationResults.Count; jj++) { Console.WriteLine(" {0}: {1}", jj, historyUpdateResult.OperationResults[jj]); } Console.WriteLine(""); } } } } }