/// <summary> /// Verifies that the variable or variable type attributes are consistent with each other. /// </summary> private bool VerifyVariableBaseConsistency(IVariableBase node) { INode datatype = Session.NodeCache.Find(node.DataType) as INode; if (datatype == null || datatype.NodeClass != NodeClass.DataType) { Log( "DataType is not recognized for Node '{0}'. NodeId = {1}, DataType = {2}", node, node.NodeId, node.DataType); return(false); } if (node.ValueRank < -3) { Log( "ValueRank is invalid for Node '{0}'. NodeId = {1}, ValueRank = {2}", node, node.NodeId, node.ValueRank); return(false); } if (node.ValueRank <= 0) { if (node.ArrayDimensions != null && node.ArrayDimensions.Count > 0) { Log( "ArrayDimensions not allowed for variable length values Node '{0}'. NodeId = {1}, ValueRank = {2}, ArrayDimensions = {3}", node, node.NodeId, node.ValueRank, new Variant(node.ArrayDimensions)); return(false); } } else { if (node.ArrayDimensions != null && node.ArrayDimensions.Count > 0 && node.ArrayDimensions.Count != node.ValueRank) { Log( "ArrayDimensions length does not match ValueRank values Node '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", node, node.NodeId, node.ValueRank, node.ArrayDimensions.Count); return(false); } } return(true); }
/// <summary> /// Initializes the control. /// </summary> public void Initialize(Session session, NodeId typeId) { ItemsLV.Items.Clear(); AdjustColumns(); if (session == null) { return; } ILocalNode root = session.NodeCache.Find(typeId) as ILocalNode; if (root == null) { return; } m_session = session; SortedDictionary <string, InstanceDeclaration> instances = new SortedDictionary <string, InstanceDeclaration>(); InstanceDeclaration declaration = new InstanceDeclaration(); declaration.Instance = root; declaration.DisplayPath = Utils.Format("({0})", root.NodeClass); declaration.Description = Utils.Format("{0}", root.Description); declaration.DataType = "NodeId"; IVariableBase variable = root as IVariableBase; if (variable != null) { INode dataType = m_session.NodeCache.Find(variable.DataType); if (dataType != null) { declaration.DataType = Utils.Format("{0}", dataType); } if (variable.ValueRank >= 0) { declaration.DataType += "[]"; } } instances.Add(declaration.DisplayPath, declaration); CollectInstances(root, String.Empty, instances); foreach (InstanceDeclaration instance in instances.Values) { AddItem(instance); } AdjustColumns(); }
/// <summary> /// Verifies the index range result for a scalar value. /// </summary> private bool VerifyIndexRangeForArray(IVariableBase variable, BuiltInType type, string indexRange, DataValue result) { // allow DA status codes. if (IsDaBadStatus(result.StatusCode)) { return(true); } Array array = result.Value as Array; if (array == null) { if (result.StatusCode != StatusCodes.BadIndexRangeNoData) { Log( "Wrong error code when reading index range for array value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return(false); } return(true); } if (indexRange == "10000000:20000000") { Log( "Expected BadIndexRangeNoData for array value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return(false); } if (array.Length > 2) { Log( "Too much data return when reading index range for array value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return(false); } return(true); }
/// <summary> /// Prompts the user to edit the read request parameters for the set of nodes provided. /// </summary> public ReadValueId[] ShowDialog(Session session, params ReadValueId[] nodesToRead) { NodeBTN.Session = session; NodeBTN.SelectedReference = null; bool editNode = true; bool editAttribute = true; bool editIndexRange = true; bool editDataEncoding = true; // populate the controls. if (nodesToRead != null && nodesToRead.Length > 0) { bool nonValueAttribute = false; for (int ii = 0; ii < nodesToRead.Length; ii++) { if (nodesToRead[ii] == null) { continue; } // only show the node if all have the same node id. if (editNode) { if (NodeBTN.SelectedNode != null && nodesToRead[ii].NodeId != NodeBTN.SelectedNode) { NodeTB.Visible = false; NodeLB.Visible = false; NodeBTN.Visible = false; editNode = false; } else { NodeBTN.SelectedNode = nodesToRead[ii].NodeId; } } // only show the attribute if all have the same attribute id. if (editAttribute) { // check if any non-value attributes are present. if (nodesToRead[ii].AttributeId != Attributes.Value) { nonValueAttribute = true; } int index = (int)nodesToRead[ii].AttributeId - 1; if (AttributeCB.SelectedIndex != -1 && index != AttributeCB.SelectedIndex) { AttributeCB.Visible = false; AttributeLB.Visible = false; editAttribute = false; } else { AttributeCB.SelectedIndex = index; } } } DataEncodingCB.Items.Clear(); editIndexRange = !nonValueAttribute; IndexRangeLB.Visible = editIndexRange; IndexRangeTB.Visible = editIndexRange; if (!nonValueAttribute) { // use the index range for the first node as template. IndexRangeTB.Text = nodesToRead[0].IndexRange; // fetch the available encodings for the first node in the list from the server. IVariableBase variable = session.NodeCache.Find(nodesToRead[0].NodeId) as IVariableBase; if (variable != null) { if (session.NodeCache.IsTypeOf(variable.DataType, Opc.Ua.DataTypeIds.Structure)) { DataEncodingCB.Items.Add(new EncodingInfo()); DataEncodingCB.SelectedIndex = 0; foreach (INode encoding in session.NodeCache.Find(variable.DataType, Opc.Ua.ReferenceTypeIds.HasEncoding, false, true)) { DataEncodingCB.Items.Add(new EncodingInfo() { EncodingName = encoding.BrowseName }); if (nodesToRead[0].DataEncoding == encoding.BrowseName) { DataEncodingCB.SelectedIndex = DataEncodingCB.Items.Count - 1; } } } } } // hide the data encodings if none to select. if (DataEncodingCB.Items.Count == 0) { DataEncodingCB.Visible = false; DataEncodingLB.Visible = false; editDataEncoding = false; } } if (!editNode && !editAttribute && !editIndexRange && !editDataEncoding) { throw new ArgumentException("nodesToRead", "It is not possible to edit the current selection as a group."); } if (base.ShowDialog() != DialogResult.OK) { return(null); } // create the list of results. ReadValueId[] results = null; if (nodesToRead == null || nodesToRead.Length == 0) { results = new ReadValueId[1]; } else { results = new ReadValueId[nodesToRead.Length]; } // copy the controls into the results. for (int ii = 0; ii < results.Length; ii++) { // preserve the existing settings if they are not being changed. if (nodesToRead != null && nodesToRead.Length > 0) { results[ii] = (ReadValueId)nodesToRead[ii].Clone(); } else { results[ii] = new ReadValueId(); } // only copy results that were actually being edited. if (editNode) { results[ii].NodeId = NodeBTN.SelectedNode; } if (editAttribute) { results[ii].AttributeId = (uint)(AttributeCB.SelectedIndex + 1); } if (editIndexRange) { results[ii].ParsedIndexRange = NumericRange.Parse(IndexRangeTB.Text); if (NumericRange.Empty != results[ii].ParsedIndexRange) { results[ii].IndexRange = results[ii].ParsedIndexRange.ToString(); } else { results[ii].IndexRange = String.Empty; } } if (editDataEncoding) { results[ii].DataEncoding = null; EncodingInfo encoding = DataEncodingCB.SelectedItem as EncodingInfo; if (encoding != null) { results[ii].DataEncoding = encoding.EncodingName; } } } return(results); }
/// <summary> /// Collects the instance declarations to display in the control. /// </summary> private void CollectInstances(ILocalNode parent, string basePath, SortedDictionary <string, InstanceDeclaration> instances) { if (parent == null) { return; } IList <IReference> supertypes = parent.References.Find( ReferenceTypeIds.HasSubtype, true, false, m_session.TypeTree); for (int ii = 0; ii < supertypes.Count; ii++) { ILocalNode supertype = m_session.NodeCache.Find(supertypes[ii].TargetId) as ILocalNode; if (supertype == null) { continue; } CollectInstances(supertype, basePath, instances); } IList <IReference> children = parent.References.Find( ReferenceTypeIds.HierarchicalReferences, false, true, m_session.TypeTree); for (int ii = 0; ii < children.Count; ii++) { ILocalNode child = m_session.NodeCache.Find(children[ii].TargetId) as ILocalNode; if (child == null) { continue; } if (child.NodeClass != NodeClass.Object && child.NodeClass != NodeClass.Variable) { continue; } if (child.ModellingRule != Objects.ModellingRule_Mandatory && child.ModellingRule != Objects.ModellingRule_Optional) { continue; } string displayPath = Utils.Format("{0}", child); if (!String.IsNullOrEmpty(basePath)) { displayPath = Utils.Format("{0}/{1}", basePath, displayPath); } InstanceDeclaration declaration = new InstanceDeclaration(); declaration.Instance = child; declaration.DisplayPath = displayPath; declaration.Description = Utils.Format("{0}", child.Description); declaration.DataType = String.Empty; IVariableBase variable = child as IVariableBase; if (variable != null) { INode dataType = m_session.NodeCache.Find(variable.DataType); if (dataType != null) { declaration.DataType = Utils.Format("{0}", dataType); } if (variable.ValueRank >= 0) { declaration.DataType += "[]"; } } IObject objectn = child as IObject; if (objectn != null) { declaration.DataType = "NodeId"; } instances[displayPath] = declaration; CollectInstances(child, displayPath, instances); } }
/// <summary> /// Verifies that the variable or variable type attributes are consistent with each other. /// </summary> private bool VerifyVariableBaseConsistency(IVariableBase node) { INode datatype = Session.NodeCache.Find(node.DataType) as INode; if (datatype == null || datatype.NodeClass != NodeClass.DataType) { Log( "DataType is not recognized for Node '{0}'. NodeId = {1}, DataType = {2}", node, node.NodeId, node.DataType); return false; } if (node.ValueRank < -3) { Log( "ValueRank is invalid for Node '{0}'. NodeId = {1}, ValueRank = {2}", node, node.NodeId, node.ValueRank); return false; } if (node.ValueRank <= 0) { if (node.ArrayDimensions != null && node.ArrayDimensions.Count > 0) { Log( "ArrayDimensions not allowed for variable length values Node '{0}'. NodeId = {1}, ValueRank = {2}, ArrayDimensions = {3}", node, node.NodeId, node.ValueRank, new Variant(node.ArrayDimensions)); return false; } } else { if (node.ArrayDimensions != null && node.ArrayDimensions.Count > 0 && node.ArrayDimensions.Count != node.ValueRank) { Log( "ArrayDimensions length does not match ValueRank values Node '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", node, node.NodeId, node.ValueRank, node.ArrayDimensions.Count); return false; } } return true; }
/// <summary> /// Displays the value in the control. /// </summary> public void ShowValue( NodeId nodeId, uint attributeId, string name, object value, bool readOnly) { m_readOnly = readOnly; NavigationMENU.Items.Clear(); if (m_readOnly) { ValuesDV.EditMode = DataGridViewEditMode.EditProgrammatically; TextValueTB.ReadOnly = true; } Type type = null; // determine the expected data type for non-value attributes. if (attributeId != 0 && attributeId != Attributes.Value) { BuiltInType builtInType = TypeInfo.GetBuiltInType(Attributes.GetDataTypeId(attributeId)); int valueRank = Attributes.GetValueRank(attributeId); type = TypeInfo.GetSystemType(builtInType, valueRank); } // determine the expected data type for value attributes. else if (!NodeId.IsNull(nodeId)) { IVariableBase variable = m_session.NodeCache.Find(nodeId) as IVariableBase; if (variable != null) { BuiltInType builtInType = TypeInfo.GetBuiltInType(variable.DataType, m_session.TypeTree); int valueRank = variable.ValueRank; type = TypeInfo.GetSystemType(builtInType, valueRank); if (builtInType == BuiltInType.ExtensionObject && valueRank < 0) { type = TypeInfo.GetSystemType(variable.DataType, m_session.Factory); } } } // use the value. else if (value != null) { type = value.GetType(); } // go with default. else { type = typeof(string); } // assign a name. if (String.IsNullOrEmpty(name)) { if (attributeId != 0) { name = Attributes.GetBrowseName(attributeId); } else { name = type.Name; } } AccessInfo info = new AccessInfo(); info.Value = Utils.Clone(value); info.TypeInfo = TypeInfo.Construct(type); if (value == null && info.TypeInfo.ValueRank < 0) { info.Value = TypeInfo.GetDefaultValue(info.TypeInfo.BuiltInType); } info.Name = name; m_value = info; ShowValue(info); }
/// <summary> /// Verifies that the node attributes are consistent with each other. /// </summary> protected bool VerifyAttributeConsistency(Node node) { if (((~node.WriteMask) & node.UserWriteMask) != 0) { Log( "UserWriteMask allows more access that WriteMask for Node '{0}'. NodeId = {1}, WriteMask = {2}, UserWriteMask = {3}", node, node.NodeId, node.WriteMask, node.UserWriteMask); return(false); } IMethod method = node as IMethod; if (method != null) { if (method.UserExecutable && !method.Executable) { Log( "UserExecutable allows more access that Executable for Node '{0}'. NodeId = {1}, Executable = {2}, UserExecutable = {3}", node, node.NodeId, method.Executable, method.UserExecutable); return(false); } } IVariableBase variableBase = node as IVariableBase; if (variableBase != null) { if (!VerifyVariableBaseConsistency(variableBase)) { return(false); } } IVariable variable = node as IVariable; if (variable != null) { if (!VerifyVariableConsistency(variable)) { return(false); } } IVariableType variableType = node as IVariableType; if (variableType != null) { if (!VerifyVariableTypeConsistency(variableType)) { return(false); } } return(true); }
/// <summary> /// Prompts the user to edit the monitored item. /// </summary> public bool ShowDialog(Session session, MonitoredItem monitoredItem, bool isEvent) { if (!monitoredItem.Created) { NodeBTN.Session = session; NodeBTN.SelectedNode = monitoredItem.StartNodeId; } // hide fields not used for events. NodeLB.Visible = !monitoredItem.Created; NodeTB.Visible = !monitoredItem.Created; NodeBTN.Visible = !monitoredItem.Created; AttributeLB.Visible = !isEvent && !monitoredItem.Created; AttributeCB.Visible = !isEvent && !monitoredItem.Created; IndexRangeLB.Visible = !isEvent && !monitoredItem.Created; IndexRangeTB.Visible = !isEvent && !monitoredItem.Created; DataEncodingLB.Visible = !isEvent && !monitoredItem.Created; DataEncodingCB.Visible = !isEvent && !monitoredItem.Created; MonitoringModeLB.Visible = !monitoredItem.Created; MonitoringModeCB.Visible = !monitoredItem.Created; SamplingIntervalLB.Visible = true; SamplingIntervalUP.Visible = true; QueueSizeLB.Visible = !isEvent; QueueSizeUP.Visible = !isEvent; DiscardOldestLB.Visible = true; DiscardOldestCK.Visible = true; DeadbandTypeLB.Visible = !isEvent; DeadbandTypeCB.Visible = !isEvent; DeadbandValueLB.Visible = !isEvent; DeadbandValueUP.Visible = !isEvent; TriggerTypeLB.Visible = !isEvent; TriggerTypeCB.Visible = !isEvent; // fill in values. SamplingIntervalUP.Value = monitoredItem.SamplingInterval; DiscardOldestCK.Checked = monitoredItem.DiscardOldest; if (!isEvent) { AttributeCB.SelectedIndex = (int)(monitoredItem.AttributeId - 1); IndexRangeTB.Text = monitoredItem.IndexRange; MonitoringModeCB.SelectedItem = monitoredItem.MonitoringMode; QueueSizeUP.Value = monitoredItem.QueueSize; DataChangeFilter filter = monitoredItem.Filter as DataChangeFilter; if (filter != null) { DeadbandTypeCB.SelectedItem = (DeadbandType)filter.DeadbandType; DeadbandValueUP.Value = (decimal)filter.DeadbandValue; TriggerTypeCB.SelectedItem = filter.Trigger; } if (!monitoredItem.Created) { // fetch the available encodings for the first node in the list from the server. IVariableBase variable = session.NodeCache.Find(monitoredItem.StartNodeId) as IVariableBase; DataEncodingCB.Items.Add(new EncodingInfo()); DataEncodingCB.SelectedIndex = 0; if (variable != null) { if (session.NodeCache.IsTypeOf(variable.DataType, Opc.Ua.DataTypeIds.Structure)) { foreach (INode encoding in session.NodeCache.Find(variable.DataType, Opc.Ua.ReferenceTypeIds.HasEncoding, false, true)) { DataEncodingCB.Items.Add(new EncodingInfo() { EncodingName = encoding.BrowseName }); if (monitoredItem.Encoding == encoding.BrowseName) { DataEncodingCB.SelectedIndex = DataEncodingCB.Items.Count - 1; } } } } } } else { AttributeCB.SelectedIndex = ((int)Attributes.EventNotifier - 1); } if (base.ShowDialog() != DialogResult.OK) { return(false); } // update monitored item. if (!monitoredItem.Created) { monitoredItem.StartNodeId = NodeBTN.SelectedNode; monitoredItem.DisplayName = session.NodeCache.GetDisplayText(monitoredItem.StartNodeId); monitoredItem.RelativePath = null; monitoredItem.AttributeId = (uint)(AttributeCB.SelectedIndex + 1); monitoredItem.MonitoringMode = (MonitoringMode)MonitoringModeCB.SelectedItem; } monitoredItem.SamplingInterval = (int)SamplingIntervalUP.Value; monitoredItem.DiscardOldest = DiscardOldestCK.Checked; if (!isEvent) { if (!monitoredItem.Created) { monitoredItem.IndexRange = IndexRangeTB.Text.Trim(); monitoredItem.Encoding = ((EncodingInfo)DataEncodingCB.SelectedItem).EncodingName; } monitoredItem.QueueSize = (uint)QueueSizeUP.Value; DataChangeTrigger trigger = (DataChangeTrigger)TriggerTypeCB.SelectedItem; DeadbandType deadbandType = (DeadbandType)DeadbandTypeCB.SelectedItem; if (monitoredItem.Filter != null || deadbandType != DeadbandType.None || trigger != DataChangeTrigger.StatusValue) { DataChangeFilter filter = new DataChangeFilter(); filter.DeadbandType = (uint)deadbandType; filter.DeadbandValue = (double)DeadbandValueUP.Value; filter.Trigger = trigger; monitoredItem.Filter = filter; } } else { if (!monitoredItem.Created) { monitoredItem.IndexRange = null; monitoredItem.Encoding = null; } monitoredItem.QueueSize = 0; monitoredItem.Filter = new EventFilter(); } return(true); }
/// <summary> /// Verifies the index range result for a scalar value. /// </summary> private bool VerifyIndexRangeForScalar(IVariableBase variable, BuiltInType type, DataValue result) { // allow DA status codes to be returned before checking the if (IsDaBadStatus(result.StatusCode)) { return(true); } if (result.StatusCode != StatusCodes.BadIndexRangeInvalid) { if (type != BuiltInType.ByteString && type != BuiltInType.String && type != BuiltInType.Variant) { Log( "Wrong error code when reading index range for scalar value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return(false); } if (type == BuiltInType.String) { string value = result.Value as string; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for sting value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return(false); } } if (type == BuiltInType.ByteString) { byte[] value = result.Value as byte[]; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for ByteString value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return(false); } } if (type == BuiltInType.Variant) { Array value = result.Value as Array; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for Variant value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return(false); } } } else { if (type == BuiltInType.ByteString || type == BuiltInType.String) { Log( "Error returned reading index range for ByteString or String value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return(false); } } return(true); }
private bool VerifyDataEncoding(IVariableBase variable, BuiltInType type, QualifiedName dataEncoding, DataValue result) { // allow DA status codes to be returned before checking the if (IsDaBadStatus(result.StatusCode)) { return(true); } IList <INode> encodings = Session.NodeCache.Find( variable.DataType, ReferenceTypeIds.HasEncoding, false, true); if (StatusCode.IsBad(result.StatusCode)) { for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { Log( "Did not return a valid value for a supported data encoding '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return(false); } } return(true); } ExtensionObject extension = result.Value as ExtensionObject; if (extension != null) { for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { if (ExpandedNodeId.ToNodeId(extension.TypeId, Session.NamespaceUris) != encodings[ii].NodeId) { Log( "Did not return value with correct data encoding '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", variable, variable.NodeId, encodings[ii].NodeId, extension.TypeId); return(false); } } } } ExtensionObject[] extensions = result.Value as ExtensionObject[]; if (extensions != null) { for (int jj = 0; jj < extensions.Length; jj++) { extension = extensions[jj]; for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { if (ExpandedNodeId.ToNodeId(extension.TypeId, Session.NamespaceUris) != encodings[ii].NodeId) { Log( "Did not return value with correct data encoding '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", variable, variable.NodeId, encodings[ii].NodeId, extension.TypeId); return(false); } } } } } return(true); }
/// <summary> /// Reads the attributes, verifies the results and updates the nodes. /// </summary> private bool Read(ReadValueIdCollection nodesToRead) { bool success = true; DataValueCollection results; DiagnosticInfoCollection diagnosticInfos; RequestHeader requestHeader = new RequestHeader(); requestHeader.ReturnDiagnostics = 0; Session.Read( requestHeader, 0, TimestampsToReturn.Both, nodesToRead, out results, out diagnosticInfos); ClientBase.ValidateResponse(results, nodesToRead); ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead); // check diagnostics. if (diagnosticInfos != null && diagnosticInfos.Count > 0) { Log("Returned non-empty DiagnosticInfos array during Read."); return(false); } // check results. for (int ii = 0; ii < nodesToRead.Count; ii++) { ReadValueId request = nodesToRead[ii]; Node node = (Node)request.Handle; if (!VerifyTimestamps(node, request.AttributeId, TimestampsToReturn.Both, results[ii])) { success = false; continue; } if (request.IndexRange == null && request.DataEncoding == null) { if (StatusCode.IsBad(results[ii].StatusCode)) { if (!VerifyBadAttribute(node, request.AttributeId, results[ii].StatusCode)) { success = false; } continue; } if (!VerifyGoodAttribute(node, request.AttributeId, results[ii])) { success = false; continue; } } else if (request.IndexRange != null) { if (request.AttributeId != Attributes.Value || (node.NodeClass != NodeClass.Variable && node.NodeClass != NodeClass.VariableType)) { if (results[ii].StatusCode != StatusCodes.BadAttributeIdInvalid && results[ii].StatusCode != StatusCodes.BadIndexRangeInvalid) { Log( "IndexRange not valid for non-value attributes '{0}'. NodeId = {1}, Attribute = {2}, StatusCode = {3}", node, node.NodeId, Attributes.GetBrowseName(request.AttributeId), results[ii].StatusCode); success = false; break; } continue; } // check for variable types with no value. if (node.NodeClass == NodeClass.VariableType) { if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid) { continue; } } // index range can be ignored if the value is null. if (StatusCode.IsGood(results[ii].StatusCode) && results[ii].Value == null) { continue; } // check for access errors. if (results[ii].StatusCode == StatusCodes.BadNotReadable || results[ii].StatusCode == StatusCodes.BadUserAccessDenied) { continue; } // check the result against the expected data type. IVariableBase variable = node as IVariableBase; BuiltInType type = TypeInfo.GetBuiltInType(variable.DataType, Session.TypeTree); if (variable.ValueRank == ValueRanks.Scalar) { if (!VerifyIndexRangeForScalar(variable, type, results[ii])) { success = false; break; } } else { if (!VerifyIndexRangeForArray(variable, type, request.IndexRange, results[ii])) { success = false; break; } } } if (request.DataEncoding != null) { if (request.AttributeId != Attributes.Value || (node.NodeClass != NodeClass.Variable && node.NodeClass != NodeClass.VariableType)) { if (results[ii].StatusCode != StatusCodes.BadAttributeIdInvalid && results[ii].StatusCode != StatusCodes.BadDataEncodingInvalid && results[ii].StatusCode != StatusCodes.BadDataEncodingUnsupported) { Log( "DataEncoding not valid for non-value attributes '{0}'. NodeId = {1}, Attribute = {2}, StatusCode = {3}", node, node.NodeId, Attributes.GetBrowseName(request.AttributeId), results[ii].StatusCode); success = false; break; } continue; } // check for variable types with no value. if (node.NodeClass == NodeClass.VariableType) { if (results[ii].StatusCode == StatusCodes.BadAttributeIdInvalid) { continue; } } // data encoding can be ignored if the value is null. if (StatusCode.IsGood(results[ii].StatusCode) && results[ii].Value == null) { continue; } // check for access errors. if (results[ii].StatusCode == StatusCodes.BadNotReadable || results[ii].StatusCode == StatusCodes.BadUserAccessDenied) { continue; } // check the result against the expected data type. IVariableBase variable = node as IVariableBase; BuiltInType type = TypeInfo.GetBuiltInType(variable.DataType, Session.TypeTree); if (type != BuiltInType.ExtensionObject) { if (!IsDaBadStatus(results[ii].StatusCode)) { if (results[ii].StatusCode != StatusCodes.BadDataEncodingInvalid && results[ii].StatusCode != StatusCodes.BadDataEncodingUnsupported) { Log( "Wrong error code when reading value with data encoding '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, results[ii].StatusCode); success = false; break; } } } else { if (!VerifyDataEncoding(variable, type, request.DataEncoding, results[ii])) { success = false; break; } } } } // check attribute consistency. if (success) { Node current = null; for (int ii = 0; ii < nodesToRead.Count; ii++) { if (Object.ReferenceEquals(current, nodesToRead[ii].Handle)) { continue; } current = (Node)nodesToRead[ii].Handle; if (!VerifyAttributeConsistency(current)) { success = false; continue; } } } return(success); }
/// <summary> /// Verifies the index range result for a scalar value. /// </summary> private bool VerifyIndexRangeForScalar(IVariableBase variable, BuiltInType type, DataValue result) { // allow DA status codes to be returned before checking the if (IsDaBadStatus(result.StatusCode)) { return true; } if (result.StatusCode != StatusCodes.BadIndexRangeInvalid) { if (type != BuiltInType.ByteString && type != BuiltInType.String && type != BuiltInType.Variant) { Log( "Wrong error code when reading index range for scalar value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return false; } if (type == BuiltInType.String) { string value = result.Value as string; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for sting value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return false; } } if (type == BuiltInType.ByteString) { byte[] value = result.Value as byte[]; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for ByteString value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return false; } } if (type == BuiltInType.Variant) { Array value = result.Value as Array; if (value != null && value.Length > 2) { Log( "Too much data return when reading index range for Variant value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return false; } } } else { if (type == BuiltInType.ByteString || type == BuiltInType.String) { Log( "Error returned reading index range for ByteString or String value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return false; } } return true; }
private bool VerifyDataEncoding(IVariableBase variable, BuiltInType type, QualifiedName dataEncoding, DataValue result) { // allow DA status codes to be returned before checking the if (IsDaBadStatus(result.StatusCode)) { return true; } IList<INode> encodings = Session.NodeCache.Find( variable.DataType, ReferenceTypeIds.HasEncoding, false, true); if (StatusCode.IsBad(result.StatusCode)) { for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { Log( "Did not return a valid value for a supported data encoding '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return false; } } return true; } ExtensionObject extension = result.Value as ExtensionObject; if (extension != null) { for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { if (ExpandedNodeId.ToNodeId(extension.TypeId, Session.NamespaceUris) != encodings[ii].NodeId) { Log( "Did not return value with correct data encoding '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", variable, variable.NodeId, encodings[ii].NodeId, extension.TypeId); return false; } } } } ExtensionObject[] extensions = result.Value as ExtensionObject[]; if (extensions != null) { for (int jj = 0; jj < extensions.Length; jj++) { extension = extensions[jj]; for (int ii = 0; ii < encodings.Count; ii++) { if (encodings[ii].BrowseName == dataEncoding) { if (ExpandedNodeId.ToNodeId(extension.TypeId, Session.NamespaceUris) != encodings[ii].NodeId) { Log( "Did not return value with correct data encoding '{0}'. NodeId = {1}, Expected = {2}, Actual = {3}", variable, variable.NodeId, encodings[ii].NodeId, extension.TypeId); return false; } } } } } return true; }
/// <summary> /// Verifies the index range result for a scalar value. /// </summary> private bool VerifyIndexRangeForArray(IVariableBase variable, BuiltInType type, string indexRange, DataValue result) { // allow DA status codes. if (IsDaBadStatus(result.StatusCode)) { return true; } Array array = result.Value as Array; if (array == null) { if (result.StatusCode != StatusCodes.BadIndexRangeNoData) { Log( "Wrong error code when reading index range for array value '{0}'. NodeId = {1}, Value = {2}, StatusCode = {3}", variable, variable.NodeId, variable.Value, result.StatusCode); return false; } return true; } if (indexRange == "10000000:20000000") { Log( "Expected BadIndexRangeNoData for array value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return false; } if (array.Length > 2) { Log( "Too much data return when reading index range for array value '{0}'. NodeId = {1}, Value = {2}, ReturnedValue = {3}", variable, variable.NodeId, variable.Value, result.Value); return false; } return true; }