public void Write(
OperationContext context,
IList<WriteValue> nodesToWrite,
IList<ServiceResult> errors)
{
if (context == null) throw new ArgumentNullException("context");
if (nodesToWrite == null) throw new ArgumentNullException("nodesToWrite");
if (errors == null) throw new ArgumentNullException("errors");
#if LEGACY_CORENODEMANAGER
Dictionary<NodeId,ILocalNode> nodes = new Dictionary<NodeId,ILocalNode>();
Dictionary<IWriteDataSource,List<RequestHandle>> datasources = new Dictionary<IWriteDataSource,List<RequestHandle>>();
#endif
try
{
m_lock.Enter();
for (int ii = 0; ii < nodesToWrite.Count; ii++)
{
WriteValue nodeToWrite = nodesToWrite[ii];
// skip items that have already been processed.
if (nodeToWrite.Processed)
{
continue;
}
// look up the node.
ILocalNode node = GetLocalNode(nodeToWrite.NodeId) as ILocalNode;
if (node == null)
{
continue;
}
// owned by this node manager.
nodeToWrite.Processed = true;
if (!node.SupportsAttribute(nodeToWrite.AttributeId))
{
errors[ii] = StatusCodes.BadAttributeIdInvalid;
continue;
}
// fetch the node metadata.
NodeMetadata metadata = GetNodeMetadata(context, node, BrowseResultMask.All);
// check access.
bool writeable = true;
ServiceResult error = null;
// determine access rights.
switch (nodeToWrite.AttributeId)
{
case Attributes.NodeId:
case Attributes.NodeClass:
case Attributes.AccessLevel:
case Attributes.UserAccessLevel:
case Attributes.Executable:
case Attributes.UserExecutable:
case Attributes.EventNotifier:
{
writeable = false;
break;
}
case Attributes.Value:
{
writeable = ((metadata.AccessLevel & AccessLevels.CurrentWrite)!= 0);
break;
}
default:
{
writeable = (metadata.WriteMask & Attributes.GetMask(nodeToWrite.AttributeId)) != 0;
break;
}
}
// error if not writeable.
if (!writeable)
{
errors[ii] = StatusCodes.BadNotWritable;
continue;
}
// determine expected datatype and value rank.
NodeId expectedDatatypeId = metadata.DataType;
int expectedValueRank = metadata.ValueRank;
if (nodeToWrite.AttributeId != Attributes.Value)
{
expectedDatatypeId = Attributes.GetDataTypeId(nodeToWrite.AttributeId);
DataValue value = nodeToWrite.Value;
if (value.StatusCode != StatusCodes.Good || value.ServerTimestamp != DateTime.MinValue || value.SourceTimestamp != DateTime.MinValue)
{
errors[ii] = StatusCodes.BadWriteNotSupported;
continue;
}
expectedValueRank = ValueRanks.Scalar;
if (nodeToWrite.AttributeId == Attributes.ArrayDimensions)
{
expectedValueRank = ValueRanks.OneDimension;
}
}
// check whether value being written is an instance of the expected data type.
object valueToWrite = nodeToWrite.Value.Value;
TypeInfo typeInfo = TypeInfo.IsInstanceOfDataType(
valueToWrite,
expectedDatatypeId,
expectedValueRank,
m_server.NamespaceUris,
m_server.TypeTree);
if (typeInfo == null)
{
errors[ii] = StatusCodes.BadTypeMismatch;
continue;
}
// check index range.
if (nodeToWrite.ParsedIndexRange.Count > 0)
{
// check index range for scalars.
if (typeInfo.ValueRank < 0)
{
errors[ii] = StatusCodes.BadIndexRangeInvalid;
continue;
}
// check index range for arrays.
else
{
Array array = (Array)valueToWrite;
if (nodeToWrite.ParsedIndexRange.Count != array.Length)
{
errors[ii] = StatusCodes.BadIndexRangeInvalid;
continue;
}
}
}
#if LEGACY_CORENODEMANAGER
// check if the node must be handled by an external datasource.
if (CheckSourceHandle(node, typeof(IWriteDataSource), ii, datasources))
{
nodes[nodeToWrite.NodeId] = node;
continue;
}
#endif
// write the default value.
error = node.Write(nodeToWrite.AttributeId, nodeToWrite.Value);
if (ServiceResult.IsBad(error))
{
errors[ii] = error;
continue;
}
}
}
finally
{
m_lock.Exit();
}
#if LEGACY_CORENODEMANAGER
// check if nothing more to do.
if (datasources.Count == 0)
{
return;
}
// call the datasources.
foreach (KeyValuePair<IWriteDataSource,List<RequestHandle>> entry in datasources)
{
try
{
entry.Key.Write(
context,
entry.Value,
nodesToWrite,
errors);
// write to the default value if the source did not handle the write.
foreach (RequestHandle handle in entry.Value)
{
ServiceResult error = errors[handle.Index];
if (error == null)
{
continue;
}
if (error.Code == StatusCodes.GoodCallAgain)
{
WriteValue nodeToWrite = nodesToWrite[handle.Index];
ILocalNode node = null;
if (!nodes.TryGetValue(nodeToWrite.NodeId, out node))
{
errors[handle.Index] = StatusCodes.BadNodeIdUnknown;
continue;
}
errors[handle.Index] = node.Write(nodeToWrite.AttributeId, nodeToWrite.Value);
}
}
}
catch (Exception e)
{
ServiceResult error = ServiceResult.Create(e, StatusCodes.BadUnexpectedError, "Unexpected error while calling the IDatasource for the Node.");
foreach (RequestHandle handle in entry.Value)
{
errors[handle.Index] = error;
}
}
}
#endif
}