Professional OPC
Development Tools

logos

Subscription to events

More
28 Aug 2019 09:55 #7666 by federica
Replied by federica on topic Subscription to events
Hi,
thank you very much for you support.

I found the problem.
There was an error in the way I specify the monitored item; this line in the code:
MonitoredItemArguments->NodeDescriptor->NodeId->ExpandedText = L"nsu=http://serverName;ns=2;s=StampingStrokeParametersEventType";
If I will face other problems during the test of my application, I will inform you and I will try the options you proposed.

Thank you again!!

Best regards,
Federica

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

More
28 Aug 2019 07:48 #7665 by support
Replied by support on topic Subscription to events
Hello,
I have reviewed your code and do not see anything obviously wrong with it.

Here are some options we can do - possibly all of them.
1. Can you try your code (with modified node ID) against our demo server?
2. Will you be able to write and test a short C# program instead, against your real server - just to find whether the problem is maybe in your code (the C# is much shorter and not so error prone, and I can point you to the right example).
3. I can instruct you to take Wireshark logs of the successful (OPCExpert) and unsuccessful communication, and we will try to analyze where is the difference.

Best regards
The following user(s) said Thank You: federica

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

More
27 Aug 2019 08:47 #7659 by federica
Replied by federica on topic Subscription to events
I tried as you suggested, filtering, at first, only the standard "/SourceNode" field:
UASimpleAttributeOperandPtr UABaseEventObject_Operands_NodeId (__uuidof(UASimpleAttributeOperand)); 
UABaseEventObject_Operands_NodeId->TypeId->NodeId->StandardName = L"BaseEventType";
UABaseEventObject_Operands_NodeId->AttributeId = UAAttributeId_NodeId;
_UAAttributeFieldPtr ToUAAttributeField(__uuidof(UAAttributeField)); 
ToUAAttributeField->Operand = UABaseEventObject_Operands_NodeId;
 
_UANodeIdPtr ObjectTypeIds_BaseEventType(__uuidof(UANodeId));
ObjectTypeIds_BaseEventType->StandardName = L"BaseEventType";
_UASimpleAttributeOperandPtr UAFilterElements_SimpleAttribute(__uuidof(UASimpleAttributeOperand));
UAFilterElements_SimpleAttribute->TypeId->NodeId = ObjectTypeIds_BaseEventType;
_UABrowsePathParserPtr UABrowsePathParserPtr(__uuidof(UABrowsePathParser)); 
_bstr_t fieldName("/SourceNode");
UAFilterElements_SimpleAttribute->QualifiedNames = UABrowsePathParserPtr->ParseRelative(fieldName)->ToUAQualifiedNameCollection();
_UAAttributeFieldPtr ToUAAttributeField1(__uuidof(UAAttributeField)); 
ToUAAttributeField1->Operand = UAFilterElements_SimpleAttribute;
 
_UAAttributeFieldCollectionPtr SelectClauses(__uuidof(UAAttributeFieldCollection)); 
 
_variant_t arg1 = _variant_t((IDispatch*)ToUAAttributeField);
_variant_t arg2 = _variant_t((IDispatch*)ToUAAttributeField1);
SelectClauses->Add(arg1);
SelectClauses->Add(arg2);
 
_UAEventFilterPtr EventFilter (__uuidof(UAEventFilter)); 
EventFilter->SelectClauses = SelectClauses;
 
_UAMonitoringParametersPtr MonitoringParameters (__uuidof(UAMonitoringParameters)); 
MonitoringParameters->SamplingInterval = 1000;
MonitoringParameters->EventFilter = EventFilter;
MonitoringParameters->QueueSize = 1000;
 
_EasyUAMonitoredItemArgumentsPtr MonitoredItemArguments(__uuidof(EasyUAMonitoredItemArguments)); 
MonitoredItemArguments->EndpointDescriptor->UrlString = L"opc.tcp://serverAddress:4840";
MonitoredItemArguments->NodeDescriptor->NodeId->ExpandedText = L"nsu=http://serverName;ns=2;s=StampingStrokeParametersEventType";
MonitoredItemArguments->MonitoringParameters = MonitoringParameters;
MonitoredItemArguments->AttributeId = UAAttributeId_EventNotifier;
 
CComSafeArray<VARIANT> arguments(1);
arguments.SetAt(0, _variant_t((IDispatch*)MonitoredItemArguments));
 
LPSAFEARRAY pArguments = arguments.Detach();
ClientPtr->SubscribeMultipleMonitoredItems(&pArguments);
arguments.Attach(pArguments);
I specify "MonitoredItemArguments.NodeDescriptor.NodeId.ExpandedText" instead of "MonitoredItemArguments.NodeDescriptor.NodeId.StandardName" because "StandardName" is empty in my case.
Inside the event handler
STDMETHOD(EventNotification)(VARIANT varSender, _EasyUAEventNotificationEventArgs* pEventArgs)
        {
          long errorCode = pEventArgs->ErrorCode;
          CString errorMsg = pEventArgs->ErrorMessage;
          [...] 
         }
I receive this error message (as "errorMsg"):
OPC-UA service result - An error specific to OPC-UA service occurred.
---- SERVICE RESULT ----
Status Code: {BadNotSupported} = 0x803D0000 (2151481344)

What could be the problem?

Do you know how can I check that all parms I specify are correct (for example, using UaExpert or another tool)?

Thank you

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

More
26 Aug 2019 15:10 #7658 by support
Replied by support on topic Subscription to events
How have you subscribed? If you have used the simplified method (SubscribeEvent), it uses many defaults, and one of them is that it selects only fields that belong to BaseEventObject.

If you need different/more fields, you need to specify them using so-called Select Clauses - and use the "universal", all-capable method SubscribeMultipleMonitoredItems. You need to put together a UAAttributeFieldCollection with the attributes you are interested in, the create UAEventFilter where this collection will be in the SelectClauses property, create UAMonitoringParameters where you assign the EventFilter, and finally create UAMonitoredItemArguments where you assign the MonitoringParameters. We have some extension methods that make it easier in .NET, but in COM you need to go through the whole process.

There is currently no example for this in C++, but conceptually, you can use the following example we have made for Free Pascal (which is also COM-based):
// This example shows how to select fields for event notifications.
 
type
  TClientEventHandlers4 = class
    procedure OnEventNotification(
      Sender: TObject;
      sender0: OleVariant;
      eventArgs: _EasyUAEventNotificationEventArgs);
  end;
 
procedure TClientEventHandlers4.OnEventNotification(
  Sender: TObject;
  sender0: OleVariant;
  eventArgs: _EasyUAEventNotificationEventArgs);
 
  function ToUAAttributeField(Operand: UASimpleAttributeOperand): UAAttributeField;
  var
    AttributeField: UAAttributeField;
  begin
    AttributeField := CoUAAttributeField.Create;
    AttributeField.Operand := Operand;
    ToUAAttributeField := AttributeField;
  end;
 
  function UAFilterElements_SimpleAttribute(TypeId: UANodeId; SimpleRelativeBrowsePathString: string): UASimpleAttributeOperand;
  var
    Operand: UASimpleAttributeOperand;
    BrowsePathParser: UABrowsePathParser;
  begin
    BrowsePathParser := CoUABrowsePathParser.Create;
    Operand := CoUASimpleAttributeOperand.Create;
    Operand.TypeId.NodeId := TypeId;
    Operand.QualifiedNames := BrowsePathParser.ParseRelative(SimpleRelativeBrowsePathString).ToUAQualifiedNameCollection;
    UAFilterElements_SimpleAttribute := Operand;
  end;
 
  function ObjectTypeIds_BaseEventType: UANodeId;
  var
    NodeId: UANodeId;
  begin
    NodeId := CoUANodeId.Create;
    NodeId.StandardName := 'BaseEventType';
    ObjectTypeIds_BaseEventType := NodeId;
  end;
 
var
  Count: Cardinal;
  Element: OleVariant;
  EntryEnumerator: IEnumVariant;
  Entry: DictionaryEntry2;
  AttributeField: UAAttributeField;
  AValueResult: ValueResult;
begin
  WriteLn;
 
  // Display the event
  if eventArgs.EventData = nil then
  begin
    WriteLn(eventArgs.ToString);
    Exit;
  end;
  WriteLn('All fields:');
  EntryEnumerator := eventArgs.EventData.FieldResults.GetEnumerator;
  while EntryEnumerator.Next(1, Element, Count) = S_OK do
  begin
    Entry := IUnknown(Element) as DictionaryEntry2;
    AttributeField := IUnknown(Entry.key) as UAAttributeField;
    AValueResult := IUnknown(Entry.Value) as ValueResult;
    WriteLn('  ', AttributeField.ToString, ' -> ', AValueResult.ToString);
  end;
  // Extracting a specific field using an event type ID and a simple relative path
  WriteLn('Source name: ',
    eventArgs.EventData.FieldResults[ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/SourceName'))].ToString);
  WriteLn('Message: ',
    eventArgs.EventData.FieldResults[ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/Message'))].ToString);
end;
 
class procedure SelectClauses.Main;
 
  function ToUAAttributeField(Operand: UASimpleAttributeOperand): UAAttributeField;
  var
    AttributeField: UAAttributeField;
  begin
    AttributeField := CoUAAttributeField.Create;
    AttributeField.Operand := Operand;
    ToUAAttributeField := AttributeField;
  end;
 
  function ObjectTypeIds_BaseEventType: UANodeId;
  var
    NodeId: UANodeId;
  begin
    NodeId := CoUANodeId.Create;
    NodeId.StandardName := 'BaseEventType';
    ObjectTypeIds_BaseEventType := NodeId;
  end;
 
  function UABaseEventObject_Operands_NodeId: UASimpleAttributeOperand;
  var
    Operand: UASimpleAttributeOperand;
  begin
    Operand := CoUASimpleAttributeOperand.Create;
    Operand.TypeId.NodeId.StandardName := 'BaseEventType';
    Operand.AttributeId := UAAttributeId_NodeId;
    UABaseEventObject_Operands_NodeId := Operand;
  end;
 
  function UAFilterElements_SimpleAttribute(TypeId: UANodeId; SimpleRelativeBrowsePathString: string): UASimpleAttributeOperand;
  var
    Operand: UASimpleAttributeOperand;
    BrowsePathParser: UABrowsePathParser;
  begin
    BrowsePathParser := CoUABrowsePathParser.Create;
    Operand := CoUASimpleAttributeOperand.Create;
    Operand.TypeId.NodeId := TypeId;
    Operand.QualifiedNames := BrowsePathParser.ParseRelative(SimpleRelativeBrowsePathString).ToUAQualifiedNameCollection;
    UAFilterElements_SimpleAttribute := Operand;
  end;
 
var
  Arguments: OleVariant;
  EvsClient: TEvsEasyUAClient;
  Client: EasyUAClient;
  ClientEventHandlers: TClientEventHandlers4;
  Handle: Cardinal;
  HandleArray: OleVariant;
  MonitoredItemArguments: EasyUAMonitoredItemArguments;
  MonitoringParameters: UAMonitoringParameters;
  EventFilter: UAEventFilter;
  SelectClauses: UAAttributeFieldCollection;
begin
  // Instantiate the client object and hook events
  EvsClient := TEvsEasyUAClient.Create(nil);
  Client := EvsClient.ComServer;
  ClientEventHandlers := TClientEventHandlers4.Create;
  EvsClient.OnEventNotification := @ClientEventHandlers.OnEventNotification;
 
  WriteLn('Subscribing...');
 
  SelectClauses := CoUAAttributeFieldCollection.Create;
  // Select specific fields using an event type ID and a simple relative path
  SelectClauses.Add(ToUAAttributeField(UABaseEventObject_Operands_NodeId));
  SelectClauses.Add(ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/SourceNode')));
  SelectClauses.Add(ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/SourceName')));
  SelectClauses.Add(ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/Time')));
  SelectClauses.Add(ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/Message')));
  SelectClauses.Add(ToUAAttributeField(UAFilterElements_SimpleAttribute(ObjectTypeIds_BaseEventType, '/Severity')));
 
  EventFilter := CoUAEventFilter.Create;
  EventFilter.SelectClauses := SelectClauses;
 
  MonitoringParameters := CoUAMonitoringParameters.Create;
  MonitoringParameters.SamplingInterval := 1000;
  MonitoringParameters.EventFilter := EventFilter;
  MonitoringParameters.QueueSize := 1000;
 
  MonitoredItemArguments := CoEasyUAMonitoredItemArguments.Create;
  MonitoredItemArguments.EndpointDescriptor.UrlString := 'opc.tcp://opcua.demo-this.com:62544/Quickstarts/AlarmConditionServer';
  MonitoredItemArguments.NodeDescriptor.NodeId.StandardName := 'Server';
  MonitoredItemArguments.MonitoringParameters := MonitoringParameters;
  //MonitoredItemArguments.SubscriptionParameters.PublishingInterval := 0;
  MonitoredItemArguments.AttributeId := UAAttributeId_EventNotifier;
 
  Arguments := VarArrayCreate([0, 0], varVariant);
  Arguments[0] := MonitoredItemArguments;
 
  TVarData(HandleArray).VType := varArray or varVariant;
  TVarData(HandleArray).VArray := PVarArray(
    Client.SubscribeMultipleMonitoredItems(PSafeArray(TVarData(Arguments).VArray)));
 
  WriteLn('Processing event notifications for 30 seconds...');
  PumpSleep(30*1000);
 
  WriteLn('Unsubscribing...');
  Client.UnsubscribeAllMonitoredItems;
 
  WriteLn('Waiting for 5 seconds...');
  PumpSleep(5*1000);
 
  WriteLn('Done...');
end;
Best regards
The following user(s) said Thank You: federica

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

More
26 Aug 2019 14:01 #7657 by federica
Replied by federica on topic Subscription to events
Now I can access to the fields (using "GetEnumerator" method and iterating through the dictionary, using the result of this function; I have some problem initializing a proper _UAAttributeField object and using "GetItem" funcion as you suggested).
But the field I receive are only the "base" fields.

I can access to some fields (heighlighted in green in the attached picture, as "/Message", "/Severity", "/EventId" or"/SourceNode") but not to the fields specific of my event (heighlighted in red in the attached picture).


It seem that the fields specific of the "StampingStrokeParametersEvent" are skipped and not available inside the _EasyUAEventNotificationEventArgs object. Is it possibile?
Attachments:

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

More
03 Aug 2019 08:22 - 03 Aug 2019 08:22 #7606 by support
Replied by support on topic Subscription to events
The KeyValuePair comes from the enumeration of the dictionary, that's true. But GetItem returns _ValueResult for the field you have specified as input _UAAttributeField. If you have managed to put together good _UAAttributeField for which GetItem succeeds, then I think you have already made the most tricky part.

opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...ieldResultDictionary~Item.html

Have you tried casting the GetItem result to _ValueResultPtr, and what were the results?

Best regards
Last edit: 03 Aug 2019 08:22 by support.

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

More
02 Aug 2019 13:15 #7601 by federica
Replied by federica on topic Subscription to events
Hi,
I receive no error but I can't cast the result of "GetItem" to any valid object because it should be a "KeyValuePair" object, I think (_UAAttributeField in the Key and _ValueResult in the Value, as you said), but "KeyValuePair" is not a COM object (as you can see from the definition: docs.microsoft.com/it-it/dotnet/api/system.collections.gener...uepair-2?view=netframework-4.8 ).

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

More
01 Aug 2019 07:00 #7592 by support
Replied by support on topic Subscription to events
Hello.
What error do you get from GetItem?

It should also be possible to enumerate the contents of the dictionary. The entries coming form the enumeration should have _UAAttributeField in the Key and _ValueResult in the Value.

Best regards

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

More
31 Jul 2019 14:13 #7589 by federica
Replied by federica on topic Subscription to events
Hi,
I'm sorry for my late answer.

I update you about my problem.

I developed a test application written in C++ (MFC C++, not managed C++) where I subscribe for events generated from the server.
I have a method which is called every time an event is generated from the server.
With the event, I receive some fields.
Now I have problem accessing this fields.

I have an "_UAFieldResultDictionaryPtr" object which contains all the fields but I can't extract data from this structure.

I also tried searching for an "_UAAttributeFieldPtr" object inside this structure using "GetItem" method but with no success...

Help please...!

Thank you,
Federica

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

More
12 Jul 2019 07:00 #7518 by support
Replied by support on topic Subscription to events
Hello,
please describe what the problem is. I do not quite understand what you mean by "I'm trying to use "SubscribeEvents" method but I can't use it in my C++ COM application.".

Certainly SubscribeEvents can be used from C++. Is the issue that you do not know what QuickOPC calls to make, or is the issue that you do not know how to translate it to C++? Or is it thaty you do not quite understand the Eventing in OPC UA in general?
Because, we do have examples for SubscribeEvents - but it's true that currently, there are none in C++. This is because the code is by far the longest and ugliest to write, from all the languages... Have you had a look at following examples (in other languages)?

- opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...20to%20a%20single%20event.html
- opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...20to%20a%20single%20event.html
- opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...20to%20a%20single%20event.html

Some of these particular examples are also provided in Free Pascal, Object Pascal, VBScript or VB6, which are all COM based, meaning that in C++ you need the same sequence of calls, just written differently.

Best regards

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

Moderators: support
Time to create page: 0.248 seconds

      

 Recommend this on Google