mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-25 11:50:03 +08:00
686 lines
22 KiB
C#
686 lines
22 KiB
C#
/* ========================================================================
|
|
* 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 System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.IO;
|
|
using System.Runtime.Serialization;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
|
|
#pragma warning disable 0618
|
|
|
|
namespace Opc.Ua.Server
|
|
{
|
|
#if LEGACY_CORENODEMANAGER
|
|
/// <summary>
|
|
/// The state of a state machine.
|
|
/// </summary>
|
|
[Obsolete("The MethodSource class is obsolete and is not supported. See Opc.Ua.MethodState for a replacement.")]
|
|
public class MethodSource : BaseInstanceSource, ICallable, IMethod
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Initializes the object with default values.
|
|
/// </summary>
|
|
public MethodSource(IServerInternal server, NodeSource parent) : base(server, parent)
|
|
{
|
|
m_arguments = new MethodArguments();
|
|
m_callbacks = new Dictionary<NodeId,CallbackParameters>();
|
|
m_executable = m_userExecutable = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new instance of the node.
|
|
/// </summary>
|
|
public static MethodSource Construct(
|
|
IServerInternal server,
|
|
NodeSource parent,
|
|
NodeId referenceTypeId,
|
|
NodeId nodeId,
|
|
QualifiedName browseName,
|
|
uint numericId)
|
|
{
|
|
MethodSource instance = new MethodSource(server, parent);
|
|
instance.Initialize(referenceTypeId, nodeId, browseName, numericId, null);
|
|
return instance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the object with default values.
|
|
/// </summary>
|
|
public MethodSource(IServerInternal server, MethodArguments arguments) : base(server, null)
|
|
{
|
|
if (arguments == null) throw new ArgumentNullException("arguments");
|
|
|
|
m_arguments = arguments;
|
|
m_callbacks = new Dictionary<NodeId,CallbackParameters>();
|
|
m_executable = m_userExecutable = true;
|
|
}
|
|
#endregion
|
|
|
|
#region ICloneable Members
|
|
/// <summary cref="NodeSource.Clone(NodeSource)" />
|
|
public override NodeSource Clone(NodeSource parent)
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
MethodSource clone = new MethodSource(Server, parent);
|
|
clone.Initialize(this);
|
|
return clone;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region INode Members
|
|
/// <summary cref="INode.NodeClass" />
|
|
public override NodeClass NodeClass
|
|
{
|
|
get
|
|
{
|
|
return NodeClass.Method;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ILocalNode Members
|
|
/// <summary cref="ILocalNode.SupportsAttribute" />
|
|
public override bool SupportsAttribute(uint attributeId)
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
switch (attributeId)
|
|
{
|
|
case Attributes.Executable:
|
|
case Attributes.UserExecutable:
|
|
{
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return base.SupportsAttribute(attributeId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region IMethod Members
|
|
/// <summary cref="IMethod.Executable" />
|
|
public bool Executable
|
|
{
|
|
get
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
return m_executable;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
m_executable = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary cref="IMethod.UserExecutable" />
|
|
public bool UserExecutable
|
|
{
|
|
get
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
return m_userExecutable;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
m_userExecutable = value;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region ICallable Members
|
|
/// <summary cref="ICallable.Call" />
|
|
public virtual ServiceResult Call(
|
|
OperationContext context,
|
|
NodeId methodId,
|
|
object methodHandle,
|
|
NodeId objectId,
|
|
IList<object> inputArguments,
|
|
IList<ServiceResult> argumentErrors,
|
|
IList<object> outputArguments)
|
|
{
|
|
// find the method to call for the object.
|
|
CallbackParameters callbackParameters = null;
|
|
|
|
lock (DataLock)
|
|
{
|
|
if (!m_executable || !m_userExecutable)
|
|
{
|
|
return StatusCodes.BadUserAccessDenied;
|
|
}
|
|
|
|
if (!m_callbacks.TryGetValue(objectId, out callbackParameters))
|
|
{
|
|
return ServiceResult.Create(StatusCodes.BadInternalError, "No handler specified for the method.");
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
// dereference extension objects.
|
|
List<object> convertedArguments = new List<object>();
|
|
|
|
foreach (object inputArgument in inputArguments)
|
|
{
|
|
ExtensionObject extension = inputArgument as ExtensionObject;
|
|
|
|
if (extension != null)
|
|
{
|
|
IEncodeable encodeable = extension.Body as IEncodeable;
|
|
|
|
if (encodeable != null)
|
|
{
|
|
convertedArguments.Add(encodeable);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
convertedArguments.Add(inputArgument);
|
|
}
|
|
|
|
// invoke the method.
|
|
Call(
|
|
context,
|
|
callbackParameters.Target,
|
|
callbackParameters.Callback,
|
|
convertedArguments,
|
|
argumentErrors,
|
|
outputArguments);
|
|
|
|
// check for argument errors.
|
|
foreach (ServiceResult argumentError in argumentErrors)
|
|
{
|
|
if (ServiceResult.IsBad(argumentError))
|
|
{
|
|
return StatusCodes.BadInvalidArgument;
|
|
}
|
|
}
|
|
|
|
// everthing ok.
|
|
return ServiceResult.Good;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
return new ServiceResult(e);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the specified method.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returning non-good results in the argumentErrors list indicates that the call was not processed.
|
|
/// Other types of errors are reported by throwing an exception.
|
|
/// </remarks>
|
|
protected virtual void Call(
|
|
OperationContext context,
|
|
NodeSource target,
|
|
Delegate methodToCall,
|
|
IList<object> inputArguments,
|
|
IList<ServiceResult> argumentErrors,
|
|
IList<object> outputArguments)
|
|
{
|
|
// check for standard handler.
|
|
GenericMethodHandler GenericHandlerToCall = methodToCall as GenericMethodHandler;
|
|
|
|
if (GenericHandlerToCall != null)
|
|
{
|
|
GenericHandlerToCall(context, target, inputArguments, argumentErrors, outputArguments);
|
|
return;
|
|
}
|
|
|
|
// check for simple handler.
|
|
NoArgumentsMethodHandler SimpleHandlerToCall = methodToCall as NoArgumentsMethodHandler;
|
|
|
|
if (SimpleHandlerToCall != null)
|
|
{
|
|
SimpleHandlerToCall(context, target);
|
|
return;
|
|
}
|
|
|
|
// subclass may define different handlers.
|
|
throw new ServiceResultException(StatusCodes.BadNotImplemented);
|
|
}
|
|
#endregion
|
|
|
|
#region Public Interface
|
|
/// <summary cref="NodeSource.Initialize(NodeSource)" />
|
|
public override void Initialize(NodeSource source)
|
|
{
|
|
lock (DataLock)
|
|
{
|
|
base.Initialize(source);
|
|
|
|
MethodSource method = (MethodSource)source;
|
|
|
|
m_arguments = method.m_arguments;
|
|
m_executable = method.m_executable;
|
|
m_userExecutable = method.m_userExecutable;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the arguments for the method.
|
|
/// </summary>
|
|
public MethodArguments Arguments
|
|
{
|
|
get { return m_arguments; }
|
|
protected set { m_arguments = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Associates an object id with a callback.
|
|
/// </summary>
|
|
public void SetCallback(NodeSource target, Delegate callback)
|
|
{
|
|
if (target == null)
|
|
{
|
|
throw new ArgumentNullException("target");
|
|
}
|
|
|
|
lock (DataLock)
|
|
{
|
|
if (callback != null)
|
|
{
|
|
m_callbacks[target.NodeId] = new CallbackParameters(target, callback);
|
|
}
|
|
else
|
|
{
|
|
m_callbacks.Remove(target.NodeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calls the method with no arguments.
|
|
/// </summary>
|
|
public void Call(OperationContext context, NodeSource target)
|
|
{
|
|
List<object> inputArguments = new List<object>();
|
|
List<ServiceResult> argumentErrors = new List<ServiceResult>();
|
|
List<object> outputArguments = new List<object>();
|
|
|
|
ServiceResult result = Call(
|
|
context,
|
|
NodeId,
|
|
null,
|
|
target.NodeId,
|
|
inputArguments,
|
|
argumentErrors,
|
|
outputArguments);
|
|
|
|
if (ServiceResult.IsBad(result))
|
|
{
|
|
throw new ServiceResultException(result);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Overridden Methods
|
|
/// <summary cref="NodeSource.CreateNode" />
|
|
protected override void CreateNode(NodeId parentId, NodeId referenceTypeId)
|
|
{
|
|
CheckNodeManagerState();
|
|
|
|
MethodAttributes attributes = new MethodAttributes();
|
|
|
|
attributes.SpecifiedAttributes = (uint)NodeAttributesMask.DisplayName;
|
|
attributes.DisplayName = DisplayName;
|
|
|
|
NodeManager.CreateMethod(
|
|
parentId,
|
|
referenceTypeId,
|
|
NodeId,
|
|
BrowseName,
|
|
attributes);
|
|
}
|
|
|
|
/// <summary cref="NodeSource.UpdateAttributes" />
|
|
protected override void UpdateAttributes(ILocalNode source)
|
|
{
|
|
base.UpdateAttributes(source);
|
|
|
|
IMethod method = source as IMethod;
|
|
|
|
if (method != null)
|
|
{
|
|
m_executable = method.Executable;
|
|
m_userExecutable = method.UserExecutable;
|
|
}
|
|
}
|
|
|
|
/// <summary cref="NodeSource.ReadAttribute" />
|
|
protected override ServiceResult ReadAttribute(IOperationContext context, uint attributeId, DataValue value)
|
|
{
|
|
switch (attributeId)
|
|
{
|
|
case Attributes.Executable:
|
|
{
|
|
if (m_callbacks.Count == 0)
|
|
{
|
|
value.Value = false;
|
|
break;
|
|
}
|
|
|
|
value.Value = Executable;
|
|
break;
|
|
}
|
|
|
|
case Attributes.UserExecutable:
|
|
{
|
|
if (!Executable || m_callbacks.Count == 0)
|
|
{
|
|
value.Value = false;
|
|
break;
|
|
}
|
|
|
|
value.Value = UserExecutable;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return base.ReadAttribute(context, attributeId, value);
|
|
}
|
|
}
|
|
|
|
return ServiceResult.Good;
|
|
}
|
|
|
|
/// <summary cref="NodeSource.WriteAttribute" />
|
|
protected override ServiceResult WriteAttribute(uint attributeId, DataValue value)
|
|
{
|
|
// check for status/timestamp writes.
|
|
if (value.StatusCode != StatusCodes.Good || value.ServerTimestamp != DateTime.MinValue || value.SourceTimestamp != DateTime.MinValue)
|
|
{
|
|
return StatusCodes.BadWriteNotSupported;
|
|
}
|
|
|
|
switch (attributeId)
|
|
{
|
|
case Attributes.Executable:
|
|
{
|
|
Executable = (bool)value.Value;
|
|
break;
|
|
}
|
|
|
|
case Attributes.UserExecutable:
|
|
{
|
|
UserExecutable = (bool)value.Value;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return base.WriteAttribute(attributeId, value);
|
|
}
|
|
}
|
|
|
|
return ServiceResult.Good;
|
|
}
|
|
#endregion
|
|
|
|
#region CallbackParameters
|
|
/// <summary>
|
|
/// Stores the parameters associated with a callback.
|
|
/// </summary>
|
|
private class CallbackParameters
|
|
{
|
|
public NodeSource Target;
|
|
public Delegate Callback;
|
|
|
|
public CallbackParameters(NodeSource target, Delegate callback)
|
|
{
|
|
Target = target;
|
|
Callback = callback;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
private bool m_executable;
|
|
private bool m_userExecutable;
|
|
private MethodArguments m_arguments;
|
|
private Dictionary<NodeId,CallbackParameters> m_callbacks;
|
|
#endregion
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Handles a method call which has input/output arguments defined.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returning non-good results in the argumentErrors list indicates that the call was not processed.
|
|
/// Other types of errors are reported by throwing an exception.
|
|
/// </remarks>
|
|
public delegate void GenericMethodHandler(
|
|
OperationContext context,
|
|
NodeSource target,
|
|
IList<object> inputArguments,
|
|
IList<ServiceResult> argumentErrors,
|
|
IList<object> outputArguments);
|
|
|
|
/// <summary>
|
|
/// Handles a method call which has no arguments defined.
|
|
/// </summary>
|
|
public delegate void NoArgumentsMethodHandler(
|
|
OperationContext context,
|
|
NodeSource target);
|
|
|
|
#region MethodArguments Class
|
|
/// <summary>
|
|
/// The state of a state machine.
|
|
/// </summary>
|
|
public class MethodArguments
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Initializes the object with default values.
|
|
/// </summary>
|
|
public MethodArguments()
|
|
{
|
|
m_inputArguments = new List<Argument>();
|
|
m_outputArguments = new List<Argument>();
|
|
}
|
|
#endregion
|
|
|
|
#region Public Interface
|
|
/// <summary>
|
|
/// The input arguments.
|
|
/// </summary>
|
|
public IList<Argument> Input
|
|
{
|
|
get { return m_inputArguments; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The output arguments.
|
|
/// </summary>
|
|
public IList<Argument> Output
|
|
{
|
|
get { return m_outputArguments; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an input argument.
|
|
/// </summary>
|
|
public void AddInput(string name, NodeId dataTypeId, int valueRank, string description)
|
|
{
|
|
Argument argument = new Argument();
|
|
|
|
argument.Name = name;
|
|
argument.DataType = dataTypeId;
|
|
argument.ValueRank = valueRank;
|
|
argument.Description = description;
|
|
|
|
m_inputArguments.Add(argument);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an output argument.
|
|
/// </summary>
|
|
public void AddOutput(string name, NodeId dataTypeId, int valueRank, string description)
|
|
{
|
|
Argument argument = new Argument();
|
|
|
|
argument.Name = name;
|
|
argument.DataType = dataTypeId;
|
|
argument.ValueRank = valueRank;
|
|
argument.Description = description;
|
|
|
|
m_outputArguments.Add(argument);
|
|
}
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
private List<Argument> m_inputArguments;
|
|
private List<Argument> m_outputArguments;
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
#region CreateObjectMethod Class
|
|
/// <summary>
|
|
/// A method used to start the simulation.
|
|
/// </summary>
|
|
public class CreateObjectMethodSource : MethodSource
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Initializes the object with default values.
|
|
/// </summary>
|
|
public CreateObjectMethodSource(IServerInternal server) : base(server, (NodeSource)null)
|
|
{
|
|
Argument arg = new Argument();
|
|
|
|
arg.Name = "ParentId";
|
|
arg.Description = "The NodeId of the Parent Node.";
|
|
arg.DataType = DataTypes.NodeId;
|
|
arg.ValueRank = ValueRanks.Scalar;
|
|
|
|
Arguments.Input.Add(arg);
|
|
|
|
arg = new Argument();
|
|
|
|
arg.Name = "ReferenceTypeId";
|
|
arg.Description = "The NodeId of the ReferenceTypeId from the Parent Node.";
|
|
arg.DataType = DataTypes.NodeId;
|
|
arg.ValueRank = ValueRanks.Scalar;
|
|
|
|
Arguments.Input.Add(arg);
|
|
|
|
arg = new Argument();
|
|
|
|
arg.Name = "BrowseName";
|
|
arg.Description = "The BrowseName of the Object to create.";
|
|
arg.DataType = DataTypes.QualifiedName;
|
|
arg.ValueRank = ValueRanks.Scalar;
|
|
|
|
Arguments.Input.Add(arg);
|
|
|
|
arg = new Argument();
|
|
|
|
arg.Name = "NodeId";
|
|
arg.Description = "The NodeId of the Object created.";
|
|
arg.DataType = DataTypes.NodeId;
|
|
arg.ValueRank = ValueRanks.Scalar;
|
|
|
|
Arguments.Output.Add(arg);
|
|
}
|
|
#endregion
|
|
|
|
#region Public Interface
|
|
/// <summary>
|
|
/// Called when the method is invoked.
|
|
/// </summary>
|
|
public delegate void MethodCallHandler(
|
|
OperationContext context,
|
|
NodeSource target,
|
|
NodeId parentId,
|
|
NodeId referenceTypeId,
|
|
QualifiedName browseName,
|
|
out NodeId objectId);
|
|
#endregion
|
|
|
|
#region Overridden Methods
|
|
/// <summary>
|
|
/// Handles a call.
|
|
/// </summary>
|
|
protected override void Call(
|
|
OperationContext context,
|
|
NodeSource target,
|
|
Delegate methodToCall,
|
|
IList<object> inputArguments,
|
|
IList<ServiceResult> argumentErrors,
|
|
IList<object> outputArguments)
|
|
{
|
|
NodeId parentId = (NodeId)inputArguments[0];
|
|
NodeId referenceTypeId = (NodeId)inputArguments[1];
|
|
QualifiedName browseName = (QualifiedName)inputArguments[2];
|
|
NodeId nodeId = null;
|
|
|
|
MethodCallHandler handler = methodToCall as MethodCallHandler;
|
|
|
|
if (handler != null)
|
|
{
|
|
handler(context, target, parentId, referenceTypeId, browseName, out nodeId);
|
|
return;
|
|
}
|
|
|
|
base.Call(context, target, methodToCall, inputArguments, argumentErrors, outputArguments);
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
#endif
|
|
}
|