Skip navigation
All Places > PI Developers Club > Blog > Authors dmoler

PI Developers Club

3 Posts authored by: dmoler Employee

There have been questions recently regarding how to acknowledge a notification programmatically.  There seem to be two common sources of confusion: which methods to use and what subscriber Guid to pass.  Acknowledgment should be done using the ANNotification object but there are several acknowledgment-related methods.  The instance methods are used on the server-side and they won't produce any effect when run from a client.  Instead the static ANNotification.AcknowledgeInstance / AcknowledgeSubscription / AddComment methods should be used.  When calling these methods a Guid identifying a subscriber must be specified to associate the comment/acknowledgment with.  This Guid should be the ID of an AFNotificationContactTemplate (delivery endpoint in the UI) that has an AFNotificationContact instance subscribed to the notification.  Note that every AFNotificationContact instance will have a corresponding Template but may or may not have an AFContact associated.

 

In the example below, I show a method that takes an AFContact and looks up the corresponding AFNotificationContact on a notification.  From the AFNotificationContact I pass in the Template.ID to the acknowledgment methods.  (UPDATE: I have modified the code slightly to get the last instance from a static ANNotification call - this is preferred to using an instance of ANNotification)

 

static void AcknowledgeActiveNotification(AFNotification notification, AFContact contact,
    ANAcknowledgmentType acknowledgmentType, string comment)
{
    // Note that to acknowledge or comment, we need to pass a AFNotificationContactTemplate (delivery endpoint in the UI)
    //   that is subscribed to the notification so that the comment/acknowledgment can be associated with that contact.
    //   This method will search a AFNotificationContacts collection to find a subscription whose AFContact matches the one we specifed.
    //   We will associate the comment/acknowledgement with this AFNotificationContact's Template.
    AFNotificationContact notificationContact = FindContactSubscription(notification.NotificationContacts, contact);

 

    // query for the last instance of the notifiation
    ANInstance lastInstance = ANNotification.GetLastInstance(notification);

 

    if (!lastInstance.IsActive)
    {
        Console.WriteLine("Last instance is inactive");
        return;
    }

 

    try
    {
        if (acknowledgmentType == ANAcknowledgmentType.Instance)
        {
            // this acknowledges the notification, even if the required acknowledgments are not met
            ANAcknowledgmentReturnStatus ackResult = ANNotification.AcknowledgeInstance(
                notification,                       // the AF notification
                lastInstance.InstanceID,            // the instance ID
                notificationContact.Template.ID,    // the ID of the AFNotificationContactTemplate
                comment);         
            Console.WriteLine("Instance acknowledgment: " + ackResult);
        }
        else if (acknowledgmentType == ANAcknowledgmentType.Subscription)
        {
            // this acknowledges a single subscription, but may not cause the notification to be acknowledged
            //   if there are > 1 required acknowledgments
            ANAcknowledgmentReturnStatus ackResult = ANNotification.AcknowledgeSubscription(
                notification,
                lastInstance.InstanceID,
                notificationContact.Template.ID,
                comment);
            Console.WriteLine("Subscription acknowledgment: " + ackResult);
        }
        else if (acknowledgmentType == ANAcknowledgmentType.Comment)
        {
            // This adds a comment, but does not perform any acknowledgment
            string errorMessage;
            bool result = ANNotification.AddComment(
                notification,
                lastInstance.InstanceID,
                notificationContact.Template.ID,
                comment,
                out errorMessage);
            Console.WriteLine("Comment added: " + result);
            if (!String.IsNullOrEmpty(errorMessage))
            {
                Console.WriteLine("Comment error: " + errorMessage);
            }
        }
    }
    catch (Exception excp)
    {
        Console.WriteLine(excp.Message);
    }
}

 

// recursively check the subscriber hierarchy looking for a AFNotificationContact associated with the specified AFContact
static AFNotificationContact FindContactSubscription(AFNotificationContacts subscribers, AFContact contact)
{
    if (contact == null || subscribers == null)
    {
        return null;
    }

 

    AFNotificationContact associatedSubscriber = null;
    foreach (AFNotificationContact subscriber in subscribers)
    {
        if (subscriber.Contact == contact)
        {
            associatedSubscriber = subscriber;
        }
        else if (subscriber.NotificationContacts != null)
        {
            // if we didn't find the contact, but this has subitems (a group or escal) we need to search them
            associatedSubscriber = FindContactSubscription(subscriber.NotificationContacts, contact);
        }

 

        if (associatedSubscriber != null)
        {
            break;
        }
    }

 

    return associatedSubscriber;
}

The PI Notifications Developers Kit includes a custom delivery channel sample that includes a delivery channel and an endpoint editor control.  Some may also want to customize the subscriber editor.  We've uploaded an addendum to get you started here:

 

DownloadCenter -> Extras -> PI Notifications XML Delivery Channel Code Samples

 

In addition to adding that control to your custom delivery endpoint project, you'll need to add an override in your custom delivery channel specifiying which control to use.  For the XML sample, this will suffice:

 

     public override Type ContentEditorType
     {
          get
          {
               return typeof(XMLDCContentEditor);
          }
     }

Writing a custom delivery channel is the most popular way of programmatically extending PI Notifications; but debugging these plug-ins can be a bit tricky.  Two common problems are 1) not running the same version of the plug-in that is loaded in Visual Studio and 2) not having remote debugging configured.

 

To ensure you are running the correct version, it is important to understand how the plug-ins are loaded.  When a process using the AFSDK requires a plug-in, the AFSDK downloads the version registered on the current AF server.  This version of the plug-in is loaded for the life of the process.  So if a new plug-in (or new version of a plug-in) is registered on the AF server, any running process will need to be restarted to load & use it.  Thus a running PI Notifications Service or PI System Explorer will need to be restarted to get the new plug-in.

 

In the early stages of developing a delivery channel, most debugging can be done with a simple test harness.  This might be a command line tool that accesses properties or a simple form that loads a UI control.  The Test() method can be tested and debugged this way, too.  When you are further along, you can test the UI elements in the PI System Explorer (PSE) by registering your plug-in with AF and (re)starting PSE.  Then you can use the Visual Studio "Attach to Process" feature (under the Debug menu) and select the "AFExplorer.exe" process.  If you don't have PSE installed on your development machine, you will need to use remote debugging.

 

If the PI Notifications Service runs locally on the development machine, simply set the service to run under your user account, then attach to it as described previously for PSE.  If you are running remotely, you will need to install the Visual Studio Remote Debugger on the machine running PI Notifications Service.  When you have a new build of your delivery channel, you will need to:

 

1.       register the new plug-in dll on the AF Server with regplugin

 

2.       copy the debug symbols (the pdb file generated with the dll) into C:\WINDOWS\Symbols\dll on the PI Notifications machine

 

3.       (re)start the PI Notifications service

 

I use a batch file that performs these steps to save time.  When all that is done and you want to debug the Send() method:

 

On the machine running the PI Notifications Service:

 

1.       run the Visual Studio Remote Debugger (you might need to delegate permissions to the account running Visual Studio depending on your security setup)

 

On the development machine:

 

2.       run "Attach to Process" in Visual Studio in the Debug menu

 

3.       in the "Qualifier" field, select Browse

 

4.       in the pop-up you should find the PI Notifications Service machine running the Remote Debugger

 

5.       select OK, then find "PIAnalyticsProcessor.exe" in the Process list and click Attach

 

If all goes well, the debugging symbols will be loaded and you will be able to set breakpoints that will be hit when a notification is triggered.

Filter Blog

By date: By tag: