Professional OPC
Development Tools

logos

Browsing issue with KepServer

More
22 Jun 2017 07:55 #5278 by m.baumgartner
Works perfectly, thanks!

Please Log in or Create an account to join the conversation.

More
20 Jun 2017 15:25 #5277 by support
Hello,
this works for me:
// This example shows how to read the DataType attributes of 3 different nodes at
// once. Using the same method, it is also possible to read multiple attributes
// of the same node.
 
class procedure ReadMultipleValues.DataType;
var
  Client: TEasyUAClient;
  ReadArguments1, ReadArguments2, ReadArguments3: _UAReadArguments;
  Arguments, Results: OleVariant;
  I: Cardinal;
  Result: _ValueResult;
begin
  // Instantiate the client object
  Client := TEasyUAClient.Create(nil);
 
  ReadArguments1 := CoUAReadArguments.Create;
  ReadArguments1.EndpointDescriptor.UrlString := 'http://opcua.demo-this.com:51211/UA/SampleServer';
  ReadArguments1.NodeDescriptor.NodeId.ExpandedText := 'nsu=http://test.org/UA/Data/;i=10845';
  ReadArguments1.AttributeId := UAAttributeId_DataType;
 
  ReadArguments2 := CoUAReadArguments.Create;
  ReadArguments2.EndpointDescriptor.UrlString := 'http://opcua.demo-this.com:51211/UA/SampleServer';
  ReadArguments2.NodeDescriptor.NodeId.ExpandedText := 'nsu=http://test.org/UA/Data/;i=10853';
  ReadArguments2.AttributeId := UAAttributeId_DataType;
 
  ReadArguments3 := CoUAReadArguments.Create;
  ReadArguments3.EndpointDescriptor.UrlString := 'http://opcua.demo-this.com:51211/UA/SampleServer';
  ReadArguments3.NodeDescriptor.NodeId.ExpandedText := 'nsu=http://test.org/UA/Data/;i=10855';
  ReadArguments3.AttributeId := UAAttributeId_DataType;
 
  Arguments := VarArrayCreate([0, 2], varVariant);
  Arguments[0] := ReadArguments1;
  Arguments[1] := ReadArguments2;
  Arguments[2] := ReadArguments3;
 
  // Obtain values. By default, the Value attributes of the nodes will be read.
  TVarData(Results).VType := varArray or varVariant;
  TVarData(Results).VArray := PVarArray(Client.ReadMultipleValues(
    PSafeArray(TVarData(Arguments).VArray)));
 
  // Display results
  for I := VarArrayLowBound(Results, 1) to VarArrayHighBound(Results, 1) do
  begin
      Result := IInterface(Results[I]) as _ValueResult;
      WriteLn;
      WriteLn('Value: ', Result.Value);
      WriteLn('Value.ExpandedText: ', Result.Value.ExpandedText);
      WriteLn('Value.NamespaceUriString: ', Result.Value.NamespaceUriString);
      WriteLn('Value.NamespaceIndex: ', Result.Value.NamespaceIndex);
      WriteLn('Value.NumericIdentifier: ', Result.Value.NumericIdentifier);
  end;
 
  // Example output:
  //
  //
  //Value: SByte
  //Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=2
  //Value.NamespaceUriString: http://opcfoundation.org/UA/
  //Value.NamespaceIndex: 0
  //Value.NumericIdentifier: 2
  //
  //Value: Float
  //Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=10
  //Value.NamespaceUriString: http://opcfoundation.org/UA/
  //Value.NamespaceIndex: 0
  //Value.NumericIdentifier: 10
  //
  //Value: String
  //Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=12
  //Value.NamespaceUriString: http://opcfoundation.org/UA/
  //Value.NamespaceIndex: 0
  //Value.NumericIdentifier: 12
end;
Regards
The following user(s) said Thank You: m.baumgartner

Please Log in or Create an account to join the conversation.

More
20 Jun 2017 14:51 #5276 by m.baumgartner
Truly sorry to come back at this again but anything that we've tried worked.
We've tried anything that seems remotely equivalent to the VB code you provided. If you can take some time to craft a quick functional delphi example it would be great.

Thank you.

Please Log in or Create an account to join the conversation.

More
20 Jun 2017 09:03 #5275 by support
Hello. No problem, please keep asking. It's good because these might be common problems and the solutions will be posted here for others to use.

Ad 1) You have already solved this - you need to use ReadMultiple or ReadMultipleValues. In .NET, we typically have several (sometimes many) method overloads, with different combinations of parameters, and using defaults for those that are not present. Unfortunately, on a COM interface you cannot have overloads - several methods with same name but different arguments. The only way to do this would be to give each method a different name - such as Read1, Read2 etc., but we intentionally decided not to do that. For each set of overloads, there always is one methods that actually does the work, and has full functionality coverage. All other methods then just prepare arguments for it and call this main method. Therefore, on COM interfaces, we just expose the main method - making it lengthier to write the code to supply all the arguments, but sill capable of doing everything needed. And, in .NET, there are also extension methods which have similar effect. So, Read and ReadValue are really just small wrappers around ReadMultiple. In fact, all UA reading can be achieved with that single ReadMultiple methods that we available to COM. And, in your own example you provided couple of posts earlier, I can see that you have resolved this by calling ReadMultiple (or ReadMultipleValues), so I assume this is no longer a problem.

ad 2) Here we are talking specifically about what to do something in Delphi. It is perfectly supported by QuickOPC COM interface, which I demonstrate by the VBScript example below (you can run it with cscript and see for yourself).
Rem This example shows how to read the DataType attributes of 3 different nodes at once. Using the same method, it is also possible 
Rem to read multiple attributes of the same node.
 
Option Explicit
 
Const UAAttributeId_DataType = 14
 
' Instantiate the client object
Dim Client: Set Client = CreateObject("OpcLabs.EasyOpc.UA.EasyUAClient")
 
Dim ReadArguments1: Set ReadArguments1 = CreateObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
ReadArguments1.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
ReadArguments1.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/;i=10845"
ReadArguments1.AttributeId = UAAttributeId_DataType
 
Dim ReadArguments2: Set ReadArguments2 = CreateObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
ReadArguments2.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
ReadArguments2.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/;i=10853"
ReadArguments2.AttributeId = UAAttributeId_DataType
 
Dim ReadArguments3: Set ReadArguments3 = CreateObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
ReadArguments3.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
ReadArguments3.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/;i=10855"
ReadArguments3.AttributeId = UAAttributeId_DataType
 
Dim arguments(2)
Set arguments(0) = ReadArguments1
Set arguments(1) = ReadArguments2
Set arguments(2) = ReadArguments3
 
' Obtain values. By default, the Value attributes of the nodes will be read.
Dim results: results = Client.ReadMultipleValues(arguments)
 
' Display results
Dim i: For i = LBound(results) To UBound(results)
    Dim ValueResult: Set ValueResult = results(i)
    WScript.Echo
    WScript.Echo "Value: " & ValueResult.Value
    WScript.Echo "Value.ExpandedText: " & ValueResult.Value.ExpandedText
    WScript.Echo "Value.NamespaceUriString: " & ValueResult.Value.NamespaceUriString
    WScript.Echo "Value.NamespaceIndex: " & ValueResult.Value.NamespaceIndex
    WScript.Echo "Value.NumericIdentifier: " & ValueResult.Value.NumericIdentifier
Next
 
' Example output:
'
'Value: SByte
'Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=2
'Value.NamespaceUriString: http://opcfoundation.org/UA/
'Value.NamespaceIndex: 0
'Value.NumericIdentifier: 2
'
'Value: Float
'Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=10
'Value.NamespaceUriString: http://opcfoundation.org/UA/
'Value.NamespaceIndex: 0
'Value.NumericIdentifier: 10
'
'Value: String
'Value.ExpandedText: nsu=http://opcfoundation.org/UA/;i=12
'Value.NamespaceUriString: http://opcfoundation.org/UA/
'Value.NamespaceIndex: 0
'Value.NumericIdentifier: 12
 

We are no Delphi experts here, but the same should be achievable in Delphi, as it goes through the same COM interfaces and is relatively powerful. Let me know if this helped enough. If not, I will try to rewrite this to Delphi; but I kind of hoped that you knew the tool better than us.

Best regards

Please Log in or Create an account to join the conversation.

More
20 Jun 2017 08:17 #5274 by m.baumgartner
Sorry to bother you again but we can't make it work for several reasons.

1) the simple Read() function does not exist in our COM/Delphi version of QuickOPC with the full list of parameters. We cannot specify the attributeID as described here : opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...Descriptor,UAAttributeId).html

2) The ReadMultiple however is available with this possibility (through UAReadArguments array). But when we try to cas the value of the result (.attributedata or .attributedata.value) as a UANodeID, we get an error. It seems that we can only cast it to string and obtain the mnemonic description of the data type.


Thank you

Please Log in or Create an account to join the conversation.

More
19 Jun 2017 17:59 #5273 by support
I am now busy with something else, so instead of trying out your code etc., I will just tell you what I think *might* be happening, and hope that it is right.

The value that you from the DataType attribute is a NodeID (our UANodeId type). And you then convert it to string. We have an implementation of this conversion that, when the NodeId is one of the standard, "well-known" node Ids, we return just its short mnemonic name. (When it is not a standard NodeId, we return something like "nsu=...;ns=...";...").

The information that you could not find is therefore in there - you just needs to access the result as UANodeId (_UANodeId), instead of converting it to string, and look at the various properties it has - such as NamespaceUriString, Identifier etc.

Best regards

Please Log in or Create an account to join the conversation.

More
19 Jun 2017 15:37 #5272 by m.baumgartner
Hello and thank you for this precision, we're almost there.

Just to clarify, we need to know the DataType in order to provide the correct operator list. Ex : AND, OR, XOR, NOT for Booleans.

Thanks to your explanations and the following code, we managed to get the data type as a String such as "Boolean" or "Double" :
    BrowseDialog := TUABrowseDialog.Create(nil);
  BrowseDialog.Mode.SelectElementType := UAElementType_Node;
  //BrowseDialog.Mode.SelectableNodeClasses := UANodeClass_Variable;
 
  BrowseDialog.Mode.MultiSelect := true;
 
 
  BrowseDialog.ShowDialog(nil);
 
 
  for i:=0 to BrowseDialog.Outputs.SelectionElements.Count - 1 do begin
 
    Element :=  BrowseDialog.Outputs.SelectionElements.Item[i];
 
    Endpoint := BrowseDialog.InputsOutputs.SelectionDescriptors[i].EndpointDescriptor.UrlString;
    Tag := BrowseDialog.InputsOutputs.SelectionDescriptors[i].NodeDescriptor.NodeId.expandedText;
 
 
 
    ClientUA := TEasyUAClient.Create(nil);
 
    ReadArgs := CoUAReadArguments.Create;
    ReadArgs.EndpointDescriptor.UrlString := Endpoint;
    ReadArgs.NodeDescriptor.NodeId.expandedText := Tag;
    ReadArgs.AttributeId := UAAttributeId_DataType;
    Arguments := VarArrayCreate([0, 0], varVariant);
    Arguments[0] := ReadArgs;
 
    TVarData(ResultType).VType := varArray or varVariant;
    TVarData(ResultType).VArray := PVarArray(ClientUA.ReadMultiple(
    PSafeArray(TVarData(Arguments).VArray)));
 
    for j := VarArrayLowBound(ResultType, 1) to VarArrayHighBound(ResultType, 1) do
    begin
      ResultAttribute := IInterface(ResultType[j]) as _UAAttributeDataResult;
      AttributeData := VarToStr(ResultAttribute.AttributeData.value) ;
      break;
    end;
 
    ClientUA.Destroy;

However, we are not able to get the ID of that datatype as you said it was defined in the OPC UA specifications (found here : github.com/OPCFoundation/UA-Nodeset/blob/master/Schema/NodeIds.csv ).

Shouldn't we be able to get 11 for Double for example? That would make it more elegant to create a mapping and would feel more standard than relying on string representation of data types.

Thanks

Please Log in or Create an account to join the conversation.

More
16 Jun 2017 14:51 - 16 Jun 2017 14:52 #5264 by support
The standard way is to read the DataType attribute of the node (and possible its ValueRank).
(Coding: Normally you read the Value attribute, this is the default but nodes have other attributes, too. You can specify them by setting AttributeId (from UAAttribtueId enumeration) in UAReadARguments that you pass to the Read call. )

What you get back by reading the DataType attribute, however, is a UA NodeId of the data type. That's precise, but somewhat cryptical. It is up to you to deal with this information. The standard datatypes have their standard NodeIds, and they are all listed in the OPC UA specification.

At places, we are using this information inside QuickOPC as well, and have internal methods to convert at least the basic DataTypeIds to .NET types, but these methods are not public, and the data types would not make direct sense Delphi either.

Please describe how you would want to use this information, so that possibly I can suggest something.
Last edit: 16 Jun 2017 14:52 by support.

Please Log in or Create an account to join the conversation.

More
16 Jun 2017 14:25 #5262 by m.baumgartner
That worked perfectly thank you.

One more thing. KepServer gives us a property called RawDataType that we can read to know which data type the tag is. It seems like your demothis server does not provide such metadata. How can we know which datatype a tag is? Is there a "standard"?

Thanks

Please Log in or Create an account to join the conversation.

More
16 Jun 2017 13:54 #5260 by support
Great - thank you for explanation.
Fortunately, the answer here is clear, and positive.

The ApplicationElement is for the "extended" information about the server - the stuff that comes from the discovery. It may, or it may not be available.

But, you can *always* access the information needed to connect to the server.

Just use UABrowseDialog.InputOutputs.SelectionDescriptors[...].EndpointDescriptor instead.

Best regards
The following user(s) said Thank You: m.baumgartner

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.212 seconds

      

 Recommend this on Google