466 lines
15 KiB
C#
466 lines
15 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2020 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 System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.ServiceModel;
|
|
using System.Runtime.Serialization;
|
|
|
|
namespace Opc.Ua.Client
|
|
{
|
|
/// <summary>
|
|
/// Stores the options to use for a browse operation.
|
|
/// </summary>
|
|
[DataContract(Namespace=Namespaces.OpcUaXsd)]
|
|
public class Browser
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Creates an unattached instance of a browser.
|
|
/// </summary>
|
|
public Browser()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates new instance of a browser and attaches it to a session.
|
|
/// </summary>
|
|
public Browser(Session session)
|
|
{
|
|
Initialize();
|
|
m_session = session;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a copy of a browser.
|
|
/// </summary>
|
|
public Browser(Browser template)
|
|
{
|
|
Initialize();
|
|
|
|
if (template != null)
|
|
{
|
|
m_session = template.m_session;
|
|
m_view = template.m_view;
|
|
m_maxReferencesReturned = template.m_maxReferencesReturned;
|
|
m_browseDirection = template.m_browseDirection;
|
|
m_referenceTypeId = template.m_referenceTypeId;
|
|
m_includeSubtypes = template.m_includeSubtypes;
|
|
m_nodeClassMask = template.m_nodeClassMask;
|
|
m_resultMask = template.m_resultMask;
|
|
m_continueUntilDone = template.m_continueUntilDone;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets all private fields to default values.
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
m_session = null;
|
|
m_view = null;
|
|
m_maxReferencesReturned = 0;
|
|
m_browseDirection = Opc.Ua.BrowseDirection.Forward;
|
|
m_referenceTypeId = null;
|
|
m_includeSubtypes = true;
|
|
m_nodeClassMask = 0;
|
|
m_resultMask = (uint)BrowseResultMask.All;
|
|
m_continueUntilDone = false;
|
|
m_browseInProgress = false;
|
|
}
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
/// <summary>
|
|
/// The session that the browse is attached to.
|
|
/// </summary>
|
|
public Session Session
|
|
{
|
|
get { return m_session; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_session = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The view to use for the browse operation.
|
|
/// </summary>
|
|
[DataMember(Order = 1)]
|
|
public ViewDescription View
|
|
{
|
|
get { return m_view; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_view = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The maximum number of refrences to return in a single browse operation.
|
|
/// </summary>
|
|
[DataMember(Order = 2)]
|
|
public uint MaxReferencesReturned
|
|
{
|
|
get { return m_maxReferencesReturned; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_maxReferencesReturned = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The direction to browse.
|
|
/// </summary>
|
|
[DataMember(Order = 3)]
|
|
public BrowseDirection BrowseDirection
|
|
{
|
|
get { return m_browseDirection; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_browseDirection = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The reference type to follow.
|
|
/// </summary>
|
|
[DataMember(Order = 4)]
|
|
public NodeId ReferenceTypeId
|
|
{
|
|
get { return m_referenceTypeId; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_referenceTypeId = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether subtypes of the reference type should be included.
|
|
/// </summary>
|
|
[DataMember(Order = 5)]
|
|
public bool IncludeSubtypes
|
|
{
|
|
get { return m_includeSubtypes; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_includeSubtypes = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The classes of the target nodes.
|
|
/// </summary>
|
|
[DataMember(Order = 6)]
|
|
public int NodeClassMask
|
|
{
|
|
get { return Utils.ToInt32(m_nodeClassMask); }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_nodeClassMask = Utils.ToUInt32(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The results to return.
|
|
/// </summary>
|
|
[DataMember(Order=6)]
|
|
public uint ResultMask
|
|
{
|
|
get { return m_resultMask; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_resultMask = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised when a browse operation halted because of a continuation point.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly")]
|
|
public event BrowserEventHandler MoreReferences
|
|
{
|
|
add { m_MoreReferences += value; }
|
|
remove { m_MoreReferences -= value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether subsequent continuation points should be processed automatically.
|
|
/// </summary>
|
|
public bool ContinueUntilDone
|
|
{
|
|
get { return m_continueUntilDone; }
|
|
|
|
set
|
|
{
|
|
CheckBrowserState();
|
|
m_continueUntilDone = value;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
/// <summary>
|
|
/// Browses the specified node.
|
|
/// </summary>
|
|
public ReferenceDescriptionCollection Browse(NodeId nodeId)
|
|
{
|
|
if (m_session == null)
|
|
{
|
|
throw new ServiceResultException(StatusCodes.BadServerNotConnected, "Cannot browse if not connected to a server.");
|
|
}
|
|
|
|
try
|
|
{
|
|
m_browseInProgress = true;
|
|
|
|
// construct request.
|
|
BrowseDescription nodeToBrowse = new BrowseDescription();
|
|
|
|
nodeToBrowse.NodeId = nodeId;
|
|
nodeToBrowse.BrowseDirection = m_browseDirection;
|
|
nodeToBrowse.ReferenceTypeId = m_referenceTypeId;
|
|
nodeToBrowse.IncludeSubtypes = m_includeSubtypes;
|
|
nodeToBrowse.NodeClassMask = m_nodeClassMask;
|
|
nodeToBrowse.ResultMask = m_resultMask;
|
|
|
|
BrowseDescriptionCollection nodesToBrowse = new BrowseDescriptionCollection();
|
|
nodesToBrowse.Add(nodeToBrowse);
|
|
|
|
// make the call to the server.
|
|
BrowseResultCollection results;
|
|
DiagnosticInfoCollection diagnosticInfos;
|
|
|
|
ResponseHeader responseHeader = m_session.Browse(
|
|
null,
|
|
m_view,
|
|
m_maxReferencesReturned,
|
|
nodesToBrowse,
|
|
out results,
|
|
out diagnosticInfos);
|
|
|
|
// ensure that the server returned valid results.
|
|
Session.ValidateResponse(results, nodesToBrowse);
|
|
Session.ValidateDiagnosticInfos(diagnosticInfos, nodesToBrowse);
|
|
|
|
// check if valid.
|
|
if (StatusCode.IsBad(results[0].StatusCode))
|
|
{
|
|
throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable);
|
|
}
|
|
|
|
// fetch initial set of references.
|
|
byte[] continuationPoint = results[0].ContinuationPoint;
|
|
ReferenceDescriptionCollection references = results[0].References;
|
|
|
|
// process any continuation point.
|
|
while (continuationPoint != null)
|
|
{
|
|
ReferenceDescriptionCollection additionalReferences;
|
|
|
|
if (!m_continueUntilDone && m_MoreReferences != null)
|
|
{
|
|
BrowserEventArgs args = new BrowserEventArgs(references);
|
|
m_MoreReferences(this, args);
|
|
|
|
// cancel browser and return the references fetched so far.
|
|
if (args.Cancel)
|
|
{
|
|
BrowseNext(ref continuationPoint, true);
|
|
return references;
|
|
}
|
|
|
|
m_continueUntilDone = args.ContinueUntilDone;
|
|
}
|
|
|
|
additionalReferences = BrowseNext(ref continuationPoint, false);
|
|
if (additionalReferences != null && additionalReferences.Count > 0)
|
|
{
|
|
references.AddRange(additionalReferences);
|
|
}
|
|
else
|
|
{
|
|
Utils.Trace("Continuation point exists, but the browse results are null/empty.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// return the results.
|
|
return references;
|
|
}
|
|
finally
|
|
{
|
|
m_browseInProgress = false;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
/// <summary>
|
|
/// Checks the state of the browser.
|
|
/// </summary>
|
|
private void CheckBrowserState()
|
|
{
|
|
if (m_browseInProgress)
|
|
{
|
|
throw new ServiceResultException(StatusCodes.BadInvalidState, "Cannot change browse parameters while a browse operation is in progress.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Fetches the next batch of references.
|
|
/// </summary>
|
|
/// <param name="continuationPoint">The continuation point.</param>
|
|
/// <param name="cancel">if set to <c>true</c> the browse operation is cancelled.</param>
|
|
/// <returns>The next batch of references</returns>
|
|
private ReferenceDescriptionCollection BrowseNext(ref byte[] continuationPoint, bool cancel)
|
|
{
|
|
ByteStringCollection continuationPoints = new ByteStringCollection();
|
|
continuationPoints.Add(continuationPoint);
|
|
|
|
// make the call to the server.
|
|
BrowseResultCollection results;
|
|
DiagnosticInfoCollection diagnosticInfos;
|
|
|
|
ResponseHeader responseHeader = m_session.BrowseNext(
|
|
null,
|
|
cancel,
|
|
continuationPoints,
|
|
out results,
|
|
out diagnosticInfos);
|
|
|
|
// ensure that the server returned valid results.
|
|
Session.ValidateResponse(results, continuationPoints);
|
|
Session.ValidateDiagnosticInfos(diagnosticInfos, continuationPoints);
|
|
|
|
// check if valid.
|
|
if (StatusCode.IsBad(results[0].StatusCode))
|
|
{
|
|
throw ServiceResultException.Create(results[0].StatusCode, 0, diagnosticInfos, responseHeader.StringTable);
|
|
}
|
|
|
|
// update continuation point.
|
|
continuationPoint = results[0].ContinuationPoint;
|
|
|
|
// return references.
|
|
return results[0].References;
|
|
}
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
private Session m_session;
|
|
private ViewDescription m_view;
|
|
private uint m_maxReferencesReturned;
|
|
private BrowseDirection m_browseDirection;
|
|
private NodeId m_referenceTypeId;
|
|
private bool m_includeSubtypes;
|
|
private uint m_nodeClassMask;
|
|
private uint m_resultMask;
|
|
private event BrowserEventHandler m_MoreReferences;
|
|
private bool m_continueUntilDone;
|
|
private bool m_browseInProgress;
|
|
#endregion
|
|
}
|
|
|
|
#region BrowserEventArgs Class
|
|
/// <summary>
|
|
/// The event arguments provided a browse operation returns a continuation point.
|
|
/// </summary>
|
|
public class BrowserEventArgs : EventArgs
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Creates a new instance.
|
|
/// </summary>
|
|
internal BrowserEventArgs(ReferenceDescriptionCollection references)
|
|
{
|
|
m_references = references;
|
|
}
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
/// <summary>
|
|
/// Whether the browse operation should be cancelled.
|
|
/// </summary>
|
|
public bool Cancel
|
|
{
|
|
get { return m_cancel; }
|
|
set { m_cancel = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether subsequent continuation points should be processed automatically.
|
|
/// </summary>
|
|
public bool ContinueUntilDone
|
|
{
|
|
get { return m_continueUntilDone; }
|
|
set { m_continueUntilDone = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The references that have been fetched so far.
|
|
/// </summary>
|
|
public ReferenceDescriptionCollection References
|
|
{
|
|
get { return m_references; }
|
|
}
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
private bool m_cancel;
|
|
private bool m_continueUntilDone;
|
|
private ReferenceDescriptionCollection m_references;
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// A delegate used to received browser events.
|
|
/// </summary>
|
|
public delegate void BrowserEventHandler(Browser sender, BrowserEventArgs e);
|
|
#endregion
|
|
}
|