How to filter OPCEvents based on Vendor Specific Attributes - 800XA usecase

Please find the link External Events to see the list of 800Xa Vendor specific example attributes list.

This method demonstrates how to filter OPC events in RTDB using vendor-specific attributes provided in a JSON expression. It:

  • Defines a time range (last 8 hours).
  • Parses a JSON string to extract attribute filters.
  • Uses VtrinLib's cJSONSerializer and cDbPropertyMask to build query masks.
  • Retrieves matching OPC events using these masks.
  • Outputs event details to the console for verification.

The approach showcases dynamic filtering based on attribute metadata and values, making it useful validating OPC event filtering queries in RTDB.

using ABB.Vtrin;
using ABB.Vtrin.IO.Serialization;

namespace VtrinLibSamples.Tests
{
    public class OPCEventsTests : BaseTest
    {
        [Test]
        public void FilterOPCEventsWithVendorSpecificAttributes()
        {
            //1. 800xa events are available in the system and being generated continuously
            DateTime currentUtcTime = DateTime.UtcNow; // Get the current UTC time once
            DateTime time1 = currentUtcTime.AddHours(-8);
            DateTime time2 = currentUtcTime.AddHours(0);

            //2. Created multiple expressions here for testing purpose, uncommented one is passed as an input
            string expressionString = "{\"*NodeName*\":\"YOUR_NODE_NAME\",\"*LongMessage*\":\"Failed to connect to target OPC Data Access server.  The OPC server did not enter running state within time limit.\"}";
            //string expressionString = "{\"*NodeName*\":\"YOUR_NODE_NAME\",\"*DeviceStatusName*\":\"Undefined\"}";
            //string expressionString = "{\"*NodeName*\":\"YOUR_NODE_NAME\"}";

            //3. Used VtrinLib inbuilt JSON Serializer instead of using Microsoft or any 3rd party library
            var jsonserializer = new cJSONSerializer();
            var jsoncontainer = jsonserializer.CreateContainer(expressionString);
            List<cDbPropertyMask> maskList = [];
            jsoncontainer.BeginDeserializeObject();
            for (; ; )
            {
                var filter = jsoncontainer.BeginDeserializeObjectField(out _);
                if (filter is null)
                    break;
                List<cDbPropertyMask> localMask = [];
                //1. Get the Instance of each Vendor specific attribute.
                //If user knows the Source Server and Category ID those also can be passed as filter to limit the search results
                var mask = new cDbPropertyMask("Attribute", cDbPropertyMask.cMaskType.WildCardStringMatch, filter);
                var instances = Driver.Classes["OpcEventAttributeDescription"].Instances.GetInstanceSet([mask]);
                string value = jsoncontainer.DeserializeString();
                //2. Construct the DB mask for each Vendor Specific Attribute with it's expected value
                foreach (var item in instances)
                {
                    var attributeMask = new cDbPropertyMask(item.Id.ToString(), cDbPropertyMask.cMaskType.Equal, value);
                    localMask.Add(attributeMask);
                }
                maskList.Add(new cDbPropertyMask(filter, cDbPropertyMask.cMaskType.OrGroup, localMask.ToArray()));
            }
            jsoncontainer.EndDeserializeObject();

            // 4. If there are any additional masks append them to maskList list
            var start = new cDbPropertyMask("EventTime", cDbPropertyMask.cMaskType.GreaterOrEqual, time1);
            var end = new cDbPropertyMask("EventTime", cDbPropertyMask.cMaskType.Less, time2);
            maskList.Add(start);
            maskList.Add(end);

            // 4. Pass the masks list as a Query to the GetInstanceSet method to fetch only required events
            var opcEvents = Driver.Classes["OPCEvent"].Instances.GetInstanceSet([.. maskList]);

            //5. Below code is just some test code as it dumps to console
            int count = 1;
            foreach (var e in opcEvents)
            {
                Console.WriteLine($"**************** Event {count} Start ****************");
                foreach (cDbPropertyInfo propertyInfo in e.Class)
                {
                    var name = string.IsNullOrWhiteSpace(propertyInfo.Name) || char.IsNumber(propertyInfo.Name[0]) ? propertyInfo.DisplayName : propertyInfo.Name;
                    var value = propertyInfo.GetValue(e);
                    if (!propertyInfo.PropertyValueHandler.EqualsDefaultValue(e))
                    {
                        //use the value
                        var displayName = Driver.Classes["OpcEvent"].GetProperty(name);
                        //Console.Write($"Display NAME HACK :: {displayName}");
                        Console.WriteLine($"Name => {propertyInfo.Name} :: Display Name => {propertyInfo.DisplayName} :: Value => {value}");
                    }
                }
                Console.WriteLine($"**************** Event {count} End ****************");
                count++;
            }
        }
    }
}