Pelco Developer Network (PDN)

GENA

GENA is a dead draft specification. This document will only cover details that are relevant to Endura related products. Consequently what is presented here may or may not diverge from Microsoft's original GENA draft specification.

Description

GENA is a notification architecture that defines the notification transmissions between various resources using the HTTP protocol. GENA has the following major goals: scalability, generalization for flexibility, high performance, and extensibility (through XML).

Use Case

A user connects to a device, subscribes to an event, receives a notification from the event, and subsequently unsubscribes from the event.

What entities are involved with Endura's GENA?

resource — Any entity, with a URI address, participating in notifications.

subscribed resource — A resource providing a notification subscription of its events.

subscriber — A resource that negotiates a subscription to a subscribed resource's events.

implied subscriber — A resource that did not negotiate a subscription to a subscribed resource, but will still be notified of events regarding the aforementioned subscribed resource.

subscription — An established relationship between two resources: subscribed resource and a subscriber.

event — A triggered notification regarding a change in a resource's state.

What actions are possible with Endura's GENA?

There are several major Endura GENA related actions:

Subscribe

This action allows a resource to subscribe to another resource's event notifications.

Example Request:
SUBSCRIBE /event/AlarmRelayConfiguration-1 HTTP/1.1
TIMEOUT: Second-300
HOST: 10.80.139.228:49152
CALLBACK: <http://10.80.139.234:8575/6210c0bf-381e-436e-bb4c-f853dd8e6c05-2/urn:pelco-com:serviceId:AlarmRelayConfiguration-1>
NT: upnp:event

SUBSCRIBE — Indicates the action requested along with the related service.

TIMEOUT — This indicates the subscription's lifespan, or more specifically when the subscription expires.

HOST — The location of the resource where a potential subscriber would like to subscribe event notifications.

CALLBACK — Shows where to deliver event notifications from the subscribed resource to the subscriber.

NT — The type of notification. All events should be UPnP typed events.

Example Response:
HTTP/1.1 200 OK
DATE: Mon, 28 Jan 2008 19:20:44 GMT
SERVER: Linux/2.4.20_mvl31-405ep_eval, UPnP/1.0, Everest/1.5
SID: uuid:1f7de802-1dd2-11b2-b22f-f180bd00fe9b
TIMEOUT: Second-300

DATE — The date time of the response.

SERVER — Describes the subscribed resource.

SID — The subscription identifier. You will need this unique identifier to renew an existing subscription. For more details please refer to the Renew description below.

TIMEOUT — This indicates the subscription's lifespan, or more specifically when the subscription expires.

Renew

This action allows a resource to maintain its subscription to another resource's event notifications. Subscriptions that are not renewed within their existing lifetimes will die. This helps maintain a subscribed resource's performance.

Example Request:
RENEW /event/AlarmArrayConfiguration-1 HTTP/1.1
TIMEOUT: Second-300
SID: uuid:b8699e2c-1dd1-11b2-b931-d488614058d6
HOST: 10.80.136.253:49152

RENEW — Indicates the action requested along with the related service.

SID — The subscription identifier. You will need this unique identifier to renew an existing subscription.

TIMEOUT — This indicates the subscription's lifespan, or more specifically when the subscription expires.

HOST — The location of the resource where a potential subscriber would like to subscribe event notifications.

SID — The subscription identifier.

Example Response:
HTTP/1.1 200 OK
DATE: Mon, 28 Jan 2008 19:20:44 GMT
SERVER: Linux/2.4.20_mvl31-405ep_eval, UPnP/1.0, Everest/1.5
SID: uuid:1f7de802-1dd2-11b2-b22f-f180bd00fe9b
TIMEOUT: Second-300

HTTP — The HTTP status code. If the request is welformed and subscribed resource is fucntioning correctly, this should be 200.

DATE — The date and time of the response.

SERVER — Describes the subscribed resource.

SID — The subscription identifier. You will need this unique identifier to renew an existing subscription. For more details please refer to the Renew description above.

TIMEOUT — This indicates the subscription's lifespan, or more specifically when the subscription expires.

Unsubscribe

This action allows a resource to unsubscribe an existing subscription to another resource's event notifications.

Example Request:
UNSUBSCRIBE /event/AlarmRelayConfiguration-1 HTTP/1.1
SID: uuid:19805e08-1dd2-11b2-b22f-f180bd00fe9b
HOST: 10.80.139.228:49152

UNSUBSCRIBE — Indicates the action requested along with the related service.

SID — The subscription identifier. You will need this unique identifier to renew an existing subscription. For more details please refer to the Renew description above.

HOST — The location of the resource where a potential subscriber would like to subscribe event notifications.

Example Response:
HTTP/1.1 200 OK
SERVER: Linux/2.4.20_mvl31-405ep_eval, UPnP/1.0, Everest/1.5
CONNECTION: close
CONTENT-LENGTH: 41
CONTENT-TYPE: text/html

<html><body><h1>200 OK</h1></body></html>

HTTP — The HTTP status code. If the request is welformed and subscribed resource is fucntioning correctly, this should be 200.

SERVER — Describes the subscribed resource.

Notify

This action allows a subscribed resource to notify a subscriber with an event notification. The notification includes event data that differs depending on the type of subscribed resource and the subscribed resource's state.

Example
NOTIFY /edfabb19-6e5e-4040-a2e8-88c25168a520Encoder-AlarmArray-1/urn:pelco-com:serviceId:AlarmArrayConfiguration-1 HTTP/1.1
HOST: 10.80.136.186:5042
CONTENT-TYPE: text/xml
CONTENT-LENGTH: 586
NT: upnp:event
NTS: upnp:propchange
SID: uuid:dc69718c-1dd1-11b2-99e7-babc06afe2f1
SEQ: 9

<e:propertyset xmlns:e="schemas-upnp-org:event-1-0" osid="" status="0" isCombined="0" requireAck="0" eventURI="uuid:edfabb19-6e5e-4040-a2e8-88c25168a520Encoder-AlarmArray-1/urn:pelco-com:serviceId:AlarmArrayConfiguration-1" serviceType="urn:schemas-pelco-com:service:AlarmArrayConfiguration:1" deviceType="urn:schemas-pelco-com:device:AlarmArray:1"eventId="10"><e:property><alarmStates><objId>990</objId><offset>0</offset><length>3</length><changed>AQA=</changed><state1>AAA=</state1><state2>AAA=</state2><enabled>BwA=</enabled></alarmStates></e:property></e:propertyset>
.

HTTP/1.1 200 OK
Content-Length: 0

RENEW — Indicates the action requested along with subscriber's callback.

HOST — The location of the subscriber

CONTENT-TYPE — Notification content will always be XML.

NT — The type of notification. All events should be UPnP typed events.

SID — The subscription identifier. You will need this unique identifier to renew an existing subscription. For more details please refer to the Renew description above.

Event Control (Subscription, Renewal and Unsubscribing)

A GENA implementation should manage subscriptions such that duplicate subscription requests are not sent. In addition a session id must be maintained for each subscription; this value is returned when a subscription is made and is required for subsequent resubscribe and unsubscribe requests.

A GENA subscription will expire based on the timeout duration specified in the TIMEOUT header of the subscription request. Events must be resubscribed to prior to the end of the expiration period.

The following code example provides a basis upon which you can design your particular GENA implementation.

Example:

// Networking calls are based on libcurl (http://curl.haxx.se/) 
#include <string>
#include <curl/curl.h> 

#define EVENT_TIMEOUT_VALUE_STRING "300"
#define CURLOPT_TIMEOUT_SECOND     4 

size_t ReadHeader(void * ptrData, size_t size, size_t nmemb, std::string * pStrHeader)
{
   size *= nmemb;
   pStrHeader->append((char *) ptrData, size);
   return size;
} 

const char * SubscribeEvents(const char * szEndPointUrl,
                             const char * szCallbackIP,
                             const char * szCallbackPort)
{
    CURL * curl = curl_easy_init();

    // Used to accumulate returned headers
    std::string strHeader;

    // Set up cURL request headers
    struct curl_slist * headers = NULL;
    std::string strTimeoutDuration = "TIMEOUT: Second-";
    strTimeoutDuration += EVENT_TIMEOUT_VALUE_STRING;
    headers = curl_slist_append(headers, strTimeoutDuration.c_str());
    std::string strCallbackURL = 
                    "CALLBACK: <http://" + szCallbackIP + ":" + szCallbackPort + "/>";
    headers = curl_slist_append(headers, strCallbackURL.c_str());
    headers = curl_slist_append(headers, "NT: upnp:event"); //UPnP header
    headers = curl_slist_append(headers, "CONTENT-TYPE: text/xml");

    // Request options
    char szErrorBuffer[CURL_ERROR_SIZE] = {0};
    CURLcode result = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, szErrorBuffer);
    result = curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURLOPT_TIMEOUT_SECOND);
    result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    result = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "SUBSCRIBE");
    result = curl_easy_setopt(curl, CURLOPT_URL, szEndPointUrl);
    result = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    result = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ReadHeader);
    result = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &strHeader);

    // Send subscription request
    result = curl_easy_perform(curl);

    // Cleanup
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    // Check results
    std::string strSid = "";
    if (result == CURLE_OK && strHeader.substr(0, 32).find("200 OK") != std::string::npos)
    {
        // Find the SID (session id) section in the results headers
        size_t posStart = strHeader.find("SID:");
        if (posStart != std::string::npos)
        {
            // Extract the session id and return for future operations on this subscription
            posStart += 5;
            size_t posEnd = strHeader.find('\r', posStart + 1);
            strSid = strHeader.substr(posStart, 
                         (posEnd == std::string::npos ? posEnd : posEnd - posStart));
        }
    }
    else
    {
        // Subscription failed; see szErrorBuffer and result for details
    }

    return strSid.c_str();
}

void RenewSubscription(const char * szEndPointUrl,
                       const char * szSessionId)
{
    CURL * curl = curl_easy_init();

    // Used to accumulate returned headers
    std::string strHeader;

    // Set up cURL request headers
    struct curl_slist * headers = NULL;
    std::string strTimeoutDuration = "TIMEOUT: Second-";
    strTimeoutDuration += EVENT_TIMEOUT_VALUE_STRING;
    headers = curl_slist_append(headers, "TIMEOUT: Second-" + szTimeout);
    headers = curl_slist_append(headers, "SID: " + szSessionId);

    // Request options   char szErrorBuffer[CURL_ERROR_SIZE] = {0};
    CURLcode result = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, szErrorBuffer);
    result = curl_easy_setopt(curl, CURLOPT_TIMEOUT, CURLOPT_TIMEOUT_SECOND);
    result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    result = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "SUBSCRIBE");
    result = curl_easy_setopt(curl, CURLOPT_URL, szEndPointUrl);
    result = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    result = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ReadHeader);
    result = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &strHeader);

    // Send renewal request
    result = curl_easy_perform(curl);

    // Cleanup
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    // Check result
    if (result != CURLE_OK || strHeader.substr(0, 32).find("200 OK") == std::string::npos)
    {
        // Resubscription failed; see szErrorBuffer and result for details
    }
}

bool UnsubscribeEvents(const char * szEndPointUrl,
                       const char * szSessionId,
                       const char * szTimeout)
{
    CURL * curl = curl_easy_init();

    // Set up cURL request headers
    std::string strHeader;
    struct curl_slist * headers = NULL;
    headers = curl_slist_append(headers, "SID: " + szSessionId);

    // Request options
    char szErrorBuffer[CURL_ERROR_SIZE] = {0};
    CURLcode result = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, szErrorBuffer);
    result = curl_easy_setopt(curl, CURLOPT_TIMEOUT, EVENT_CONTROL_CURLOPT_TIMEOUT_SECOND);
    result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    result = curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "UNSUBSCRIBE");
    result = curl_easy_setopt(curl, CURLOPT_URL, szEndPointUrl);
    result = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    result = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ReadHeader);
    result = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &strHeader);

    // Send unsubscribe request
    result = curl_easy_perform(curl);

    // Cleanup   curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    // Check result
    if (result == CURLE_OK && strHeader.substr(0, 32).find("200 OK") != std::string::npos)
    {
        return true;
    }
    else
    {
        // Unsubscribe failed; see szErrorBuffer and result for details
    }

    return false;
}