public void Call(
OperationContext context,
IList<CallMethodRequest> methodsToCall,
IList<CallMethodResult> results,
IList<ServiceResult> errors)
{
if (context == null) throw new ArgumentNullException("context");
if (methodsToCall == null) throw new ArgumentNullException("methodsToCall");
if (results == null) throw new ArgumentNullException("results");
if (errors == null) throw new ArgumentNullException("errors");
#if LEGACY_CORENODEMANAGER
List<CallRequest> callables = new List<CallRequest>();
#endif
try
{
m_lock.Enter();
for (int ii = 0; ii < methodsToCall.Count; ii++)
{
CallMethodRequest methodToCall = methodsToCall[ii];
// skip items that have already been processed.
if (methodToCall.Processed)
{
continue;
}
// look up the node.
ILocalNode node = GetLocalNode(methodToCall.ObjectId) as ILocalNode;
if (node == null)
{
continue;
}
methodToCall.Processed = true;
// look up the method.
ILocalNode method = GetLocalNode(methodToCall.MethodId) as ILocalNode;
if (method == null)
{
errors[ii] = ServiceResult.Create(StatusCodes.BadMethodInvalid, "Method is not in the address space.");
continue;
}
// check that the method is defined for the object.
if (!node.References.Exists(ReferenceTypeIds.HasComponent, false, methodToCall.MethodId, true, m_server.TypeTree))
{
errors[ii] = ServiceResult.Create(StatusCodes.BadMethodInvalid, "Method is not a component of the Object.");
continue;
}
#if LEGACY_CORENODEMANAGER
// find object to call.
ICallable callable = method as ICallable;
SourceHandle handle = method.Handle as SourceHandle;
if (handle != null)
{
callable = handle.Source as ICallable;
}
if (callable == null)
{
errors[ii] = ServiceResult.Create(StatusCodes.BadNotImplemented, "Method does not have a source registered.");
continue;
}
// get the input arguments.
IVariable argumentNode = GetLocalNode(method.NodeId, ReferenceTypes.HasProperty, false, false, BrowseNames.InputArguments) as IVariable;
// extract the arguments from the node.
Argument[] arguments = null;
if (argumentNode != null)
{
Array value = argumentNode.Value as Array;
if (value != null)
{
arguments = ExtensionObject.ToArray(value, typeof(Argument)) as Argument[];
}
}
// validate the input arguments.
bool argumentError = false;
List<ServiceResult> argumentErrors = new List<ServiceResult>();
object[] validatedArguments = new object[methodToCall.InputArguments.Count];
// check if the argument is expected.
if ((arguments == null && methodToCall.InputArguments.Count > 0) || (methodToCall.InputArguments.Count != arguments.Length))
{
errors[ii] = StatusCodes.BadArgumentsMissing;
continue;
}
for (int jj = 0; jj < methodToCall.InputArguments.Count; jj++)
{
// can't do anything if the argument definition is missing.
Argument argumentDefinition = arguments[jj];
if (argumentDefinition == null)
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadConfigurationError, "Server does not have a defintion for argument."));
argumentError = true;
continue;
}
// a null value can be used for optional arguments.
object argumentValue = methodToCall.InputArguments[jj].Value;
if (argumentValue == null)
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadInvalidArgument, "Argument cannot be null."));
argumentError = true;
continue;
}
// get the datatype.
if (!m_server.TypeTree.IsEncodingFor(argumentDefinition.DataType, argumentValue))
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadTypeMismatch, "Expecting value with datatype '{0}'.", argumentDefinition.DataType));
argumentError = true;
continue;
}
// check the array size.
Array array = argumentValue as Array;
if (array != null)
{
if (argumentDefinition.ValueRank == ValueRanks.Scalar)
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadTypeMismatch, "Expecting a scalar value."));
argumentError = true;
continue;
}
if (argumentDefinition.ValueRank > 0 && array.Length != argumentDefinition.ValueRank)
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadTypeMismatch, "Expecting an array with length {0}.", argumentDefinition.ValueRank));
argumentError = true;
continue;
}
}
else
{
if (argumentDefinition.ValueRank >= 0)
{
argumentErrors.Add(ServiceResult.Create(StatusCodes.BadTypeMismatch, "Expecting an array value."));
argumentError = true;
continue;
}
}
// argument passed initial validation.
validatedArguments[jj] = argumentValue;
argumentErrors.Add(null);
}
#else
errors[ii] = StatusCodes.BadNotImplemented;
#endif
#if LEGACY_CORENODEMANAGER
CallRequest request = new CallRequest();
request.Callable = callable;
request.Index = ii;
request.MethodId = method.NodeId;
request.MethodHandle = (handle != null)?handle.Handle:null;
request.ObjectId = node.NodeId;
request.InputArguments = validatedArguments;
request.ArgumentErrors = argumentErrors;
request.HasInvalidArgument = argumentError;
callables.Add(request);
#endif
}
}
finally
{
m_lock.Exit();
}
#if LEGACY_CORENODEMANAGER
// check if nothing to do.
if (callables.Count == 0)
{
return;
}
// call the methods.
foreach (CallRequest callable in callables)
{
// call method if no errors occurred.
List<object> outputArguments = new List<object>();
if (!callable.HasInvalidArgument)
{
try
{
errors[callable.Index] = callable.Callable.Call(
context,
callable.MethodId,
callable.MethodHandle,
callable.ObjectId,
callable.InputArguments,
callable.ArgumentErrors,
outputArguments);
}
catch (Exception e)
{
errors[callable.Index] = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Error occurred invoking method.");
}
}
else
{
errors[callable.Index] = ServiceResult.Create(StatusCodes.BadInvalidArgument, "One or more arguments were not valid.");
}
// create the result item.
CallMethodResult result = results[callable.Index] = new CallMethodResult();
// process argument errors.
bool errorExists = false;
foreach (ServiceResult argumentError in callable.ArgumentErrors)
{
if (ServiceResult.IsBad(argumentError))
{
result.InputArgumentResults.Add(argumentError.Code);
DiagnosticInfo diagnosticInfo = null;
if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0)
{
diagnosticInfo = ServerUtils.CreateDiagnosticInfo(m_server, context, argumentError);
errorExists = true;
}
result.InputArgumentDiagnosticInfos.Add(diagnosticInfo);
}
else
{
result.InputArgumentResults.Add(StatusCodes.Good);
}
}
if (!errorExists)
{
result.InputArgumentDiagnosticInfos.Clear();
}
// copy output arguments into result.
result.OutputArguments.Clear();
foreach (object outputArgument in outputArguments)
{
result.OutputArguments.Add(new Variant(outputArgument));
}
}
#endif
}