/* ======================================================================== * 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 /// /// The state of a state machine. /// [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 /// /// Initializes the object with default values. /// public MethodSource(IServerInternal server, NodeSource parent) : base(server, parent) { m_arguments = new MethodArguments(); m_callbacks = new Dictionary(); m_executable = m_userExecutable = true; } /// /// Creates a new instance of the node. /// 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; } /// /// Initializes the object with default values. /// public MethodSource(IServerInternal server, MethodArguments arguments) : base(server, null) { if (arguments == null) throw new ArgumentNullException("arguments"); m_arguments = arguments; m_callbacks = new Dictionary(); m_executable = m_userExecutable = true; } #endregion #region ICloneable Members /// public override NodeSource Clone(NodeSource parent) { lock (DataLock) { MethodSource clone = new MethodSource(Server, parent); clone.Initialize(this); return clone; } } #endregion #region INode Members /// public override NodeClass NodeClass { get { return NodeClass.Method; } } #endregion #region ILocalNode Members /// 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 /// public bool Executable { get { lock (DataLock) { return m_executable; } } set { lock (DataLock) { m_executable = value; } } } /// public bool UserExecutable { get { lock (DataLock) { return m_userExecutable; } } set { lock (DataLock) { m_userExecutable = value; } } } #endregion #region ICallable Members /// public virtual ServiceResult Call( OperationContext context, NodeId methodId, object methodHandle, NodeId objectId, IList inputArguments, IList argumentErrors, IList 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 convertedArguments = new List(); 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); } } /// /// Invokes the specified method. /// /// /// 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. /// protected virtual void Call( OperationContext context, NodeSource target, Delegate methodToCall, IList inputArguments, IList argumentErrors, IList 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 /// 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; } } /// /// Returns the arguments for the method. /// public MethodArguments Arguments { get { return m_arguments; } protected set { m_arguments = value; } } /// /// Associates an object id with a callback. /// 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); } } } /// /// Calls the method with no arguments. /// public void Call(OperationContext context, NodeSource target) { List inputArguments = new List(); List argumentErrors = new List(); List outputArguments = new List(); ServiceResult result = Call( context, NodeId, null, target.NodeId, inputArguments, argumentErrors, outputArguments); if (ServiceResult.IsBad(result)) { throw new ServiceResultException(result); } } #endregion #region Overridden Methods /// 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); } /// 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; } } /// 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; } /// 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 /// /// Stores the parameters associated with a callback. /// 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 m_callbacks; #endregion } /// /// Handles a method call which has input/output arguments defined. /// /// /// 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. /// public delegate void GenericMethodHandler( OperationContext context, NodeSource target, IList inputArguments, IList argumentErrors, IList outputArguments); /// /// Handles a method call which has no arguments defined. /// public delegate void NoArgumentsMethodHandler( OperationContext context, NodeSource target); #region MethodArguments Class /// /// The state of a state machine. /// public class MethodArguments { #region Constructors /// /// Initializes the object with default values. /// public MethodArguments() { m_inputArguments = new List(); m_outputArguments = new List(); } #endregion #region Public Interface /// /// The input arguments. /// public IList Input { get { return m_inputArguments; } } /// /// The output arguments. /// public IList Output { get { return m_outputArguments; } } /// /// Adds an input argument. /// 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); } /// /// Adds an output argument. /// 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 m_inputArguments; private List m_outputArguments; #endregion } #endregion #region CreateObjectMethod Class /// /// A method used to start the simulation. /// public class CreateObjectMethodSource : MethodSource { #region Constructors /// /// Initializes the object with default values. /// 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 /// /// Called when the method is invoked. /// public delegate void MethodCallHandler( OperationContext context, NodeSource target, NodeId parentId, NodeId referenceTypeId, QualifiedName browseName, out NodeId objectId); #endregion #region Overridden Methods /// /// Handles a call. /// protected override void Call( OperationContext context, NodeSource target, Delegate methodToCall, IList inputArguments, IList argumentErrors, IList 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 }