JMF Status Subscription using Microsoft .NET

This tutorial demonstrates how to develop a simple JDF Application for production data acquisition (PDA) in Microsoft .NET (C#). The PDA is based on the JMF Status Subscription mechanism which is used to subscribe to periodical JMF Status Signals of JDF Devices. Originally the sample was written in Microsoft Visual Studio 2010 (.NET Framework 4.0). An Express Version of Visual C# for Windows also is available for download on the Microsoft Website: (http://www.microsoft.com/germany/express/).

 

The JDF Sample Application can be checked out from our public subversion repository:

SVN WebUI: http://sourceforge.net/p/jdf4you/code
Project Name: csharp-jmf-status


Info

When developing an JDF Application you can use CIP4 Bambi as JDF Device Simulator and Development Test Framework.

Microsoft .NET, JDF / JMF and XPath…

First of all a brief practical introduction into Microsoft .NET, JDF / JMF and XPath: As common known JDF / JMF is based on XML. The XML-Default-Namespace of JDF / JMF is http://www.CIP4.org/JDFSchema_1_1. When using Microsoft .NET and XPath a special focus lies on this namespace. As shown in the first both C# Code-Snippets the XML-Default-Namespace must be extended by a prefix (here “ns”). This functionality is provided by the XmlNamespaceManager.

In Microsoft .NET XML-Files either can be opened in read-only or in full access mode. The XPathNavigation object created by a XPathDocument object provides the XML Document in read-only mode:

// additional namespaces needed
using System.Xml;
using System.Xml.XPath;
 
/// <summary>
/// Extract details of received JMF Messages by using of XPath.
/// </summary>
/// <param name="stream">Stream which contains the JMF Message.</param>
private void ExtractMsgDetails(Stream stream)
{
    // parse message stream.
    XPathDocument doc = new XPathDocument(stream);
    XPathNavigator nav = doc.CreateNavigator();
 
    // ATTENTION: Take care for the XML Default Namespace!
    // In order to use XPath the creation of a prefix (e. g. "ns") is required.
    XmlNamespaceManager nsManager = new XmlNamespaceManager(nav.NameTable);
    nsManager.AddNamespace("ns", "http://www.CIP4.org/JDFSchema_1_1");
 
    // extract message details (prefix "ns") and display.
    string xDeviceId = "/ns:JMF/ns:Signal/ns:DeviceInfo/@DeviceID";
 
    // DeviceId attribute
    string deviceId = nav.SelectSingleNode(xDeviceId, nsManager).Value;
 
    // further XPath read operations...
    // [...]
}

For XML-Document modifications the full-access mode is required. In order to achieve this the XPathNavigator object has to be created by the XMLDocument object like shown below:

// additional namespaces needed
using System.Xml;
using System.Xml.XPath;
 
/// <summary>
/// Change attributes in JMF Message Template by the using of XPath.
/// </summary>
private void UpdateMsgAttribute(string pathJMFTemplate, string listenerUrl)
{
    // load StatusSubscription template.
    XmlDocument doc = new XmlDocument();
    doc.Load(pathJMFTemplate);
    XPathNavigator nav = doc.CreateNavigator();
 
    // remind the default namespace
    XmlNamespaceManager nsManager = new XmlNamespaceManager(nav.NameTable);
    nsManager.AddNamespace("ns", "http://www.CIP4.org/JDFSchema_1_1");
 
    // replace listener url
    string xUrlListener = "/ns:JMF/ns:Query/ns:Subscription/@URL";
    nav.SelectSingleNode(xUrlListener, nsManager).SetValue(listenerUrl);
 
    // further write (and read) operations
    [...]
}

In both situations XPath expressions are executed by the XPathNavigator. Using XPathDocument as creator raises performance whereas XMLDocument allows modifications. Both mechanisms require to introduce a prefix for the XML-Default-Namespace. More information around XPath Technology is available on http://www.w3schools.com/xpath/.

HttpListener for receiving JMF Messages

JMF Messages are using the HTTP Protocol for transmission. The HttpListener object provides the creation of a HTTP listener channel in Microsoft .NET. Administrator permissions are required in order to register a listener URL in Windows 7 (and maybe earlier). The following C# Code-Snippet shows how to define the URL http://127.0.0.1:8280/jmf/ as listener:

// additional namespaces needed
using System.Net;
using System.Threading
 
// url listening to jmf messages
private readonly static string LISTENER_URL = "http://127.0.0.1:8280/jmf/";
 
// the http listener instance
private HttpListener httpListener;
 
/// <summary>
/// Creates and activates a HttpListener for receiving and processing JMF Messages.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cmdListenerOpen_Click(object sender, EventArgs e)
{
    // initialize http listener
    httpListener = new HttpListener();
    httpListener.Prefixes.Add(LISTENER_URL);
 
    // start thread for listening
    new Thread(new ThreadStart(ListenerThread)).Start();
}
 
/// <summary>
/// This method is constructed for running in a separate thread started by cmdListnerOpen_Click().
/// Is waiting for new JMF Messages and processes them when received.
/// </summary>
private void ListenerThread()
{
    // start http listener
    httpListener.Start();
 
    // listening...
    while (true)
    {
        // waiting for JMF Message
        HttpListenerContext ctx = httpListener.GetContext();
 
        // handle JMF Message
        JMFHandler(ctx);
 
        // close context (DON'T FORGET!)
        ctx.Response.Close();
    }
}

The registration of the listener URL starts with a new instance and the configuration of the HttpListener. Afterwards a new Thread (“ListenerThread”) will be created and started. The task of the new thread is to wait for JMF Messages and process them. The GetContext() method of HttpListener is waiting until a new message receives. When received JMF Messages will be processed by the JMFHandler() method (last C# Code-Snippet). Finally the context response object has to be closed explicitly before waiting for further messages.

Subscribe to JMF-Status-Messages

This topic explains how to subscribe to status messages of a JDF Device. A JDF Device can send JMF Status Signals periodically and whenever a status attribute changes. The target URL is defined in the message subscription as well. Following a template of a JMF-Query for status subscription. The parameter “{LISTENER_URL}” defines the target URL where subscribed messages has to be sent.

<?xml version="1.0" encoding="UTF-8"?>
<JMF xmlns="http://www.CIP4.org/JDFSchema_1_1" MaxVersion="1.4"
    TimeStamp="2010-06-22T18:48:19+02:00" Version="1.4"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:type="JMFRootMessage">
    <Query ID="m_100622_184819343_000000" Type="Status" xsi:type="QueryStatus">
        <Subscription RepeatTime="10" URL="{LISTENER_URL}">
            <ObservationTarget ObservationPath="*"/>
        </Subscription>
        <StatusQuParams DeviceDetails="Brief" JobDetails="Brief"/>
     </Query>
</JMF>
This C# Code-Snippet shows how to load, adjust and send a status subscription based on the message template above (file name: “StatusSubscription.tpl.jmf”):

 

// additional namespaces needed
using System.Net;
using System.Xml;
using System.Xml.XPath;
 
/// <summary>
/// Subscribe to all status messages of a JDF Device.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cmdSubscribe_Click(object sender, EventArgs e)
{
    // load StatusSubscription template.
    XmlDocument doc = new XmlDocument();
    doc.Load(AppDomain.CurrentDomain.BaseDirectory + @"Templates\StatusSubscription.tpl.jmf");
    XPathNavigator nav = doc.CreateNavigator();
 
    // remind the default namespace
    XmlNamespaceManager nsManager = new XmlNamespaceManager(nav.NameTable);
    nsManager.AddNamespace("ns", "http://www.CIP4.org/JDFSchema_1_1");
 
    // replace listener url
    string xUrlListener = "/ns:JMF/ns:Query/ns:Subscription/@URL";
    nav.SelectSingleNode(xUrlListener, nsManager).SetValue(LISTENER_URL);
 
    // create webrequest for message transmission
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(cmbDeviceUrl.Text);
    request.Method = "POST";
    request.ContentType = "application/vnd.cip4-jmf+xml";
    doc.Save(request.GetRequestStream());
 
    // send and close response
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    response.Close();
}

 

A new XMLDocument object will be created which loads the JMF message template. Using the XPathNavigator the XML-Default-Namespace will be extend by a prefix. The parameter {LISTENER_URL} will be set via an XPath expression. Finally the JMF-Query will be packed into an HttpWebRequest for transmission. The request attributes “Method” and “ContentType” has to be set to “POST” and “application/vnd.cip4-jmf+xml”.

Unsubscribe to JMF-Status-Messages

This block describes how to unsubscribe to status messages. Each subscription is defined by its target address (listener URL). Using a JMF-Command “StopPersistentChannel” a subscription can be removed from a JDF Device. Here a message template of such a JMF-Command:

<?xml version="1.0" encoding="UTF-8"?>
<JMF MaxVersion="1.4" TimeStamp="2010-06-27T17:56:04+02:00"
    Version="1.4" xmlns="http://www.CIP4.org/JDFSchema_1_1"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="JMFRootMessage">
    <Command ID="m_100627_17560591_000000" Type="StopPersistentChannel" xsi:type="CommandStopPersistentChannel">
        <StopPersChParams URL="{LISTENER_URL}"/>
    </Command>
</JMF>

Equally to the status subscription following the C# Code-Snippet how to load, adjust and send the JMF Command based on the message template above (file name: “StopPersistentChannel.tpl.jmf”):

// additional namespaces needed
using System.Net;
using System.Xml;
using System.Xml.XPath;
 
/// <summary>
/// Unsubscribe to all status messages of a JDF Device.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cmdUnsubscribe_Click(object sender, EventArgs e)
{
    // load StatusSubscription template.
    XmlDocument doc = new XmlDocument();
    doc.Load(AppDomain.CurrentDomain.BaseDirectory + @"Templates\StopPersistentChannel.tpl.jmf");
    XPathNavigator nav = doc.CreateNavigator();
 
    // remind the default namespace
    XmlNamespaceManager nsManager = new XmlNamespaceManager(nav.NameTable);
    nsManager.AddNamespace("ns", "http://www.CIP4.org/JDFSchema_1_1");
 
    // replace listener url
    string xUrlListener = "/ns:JMF/ns:Command/ns:StopPersChParams/@URL";
    nav.SelectSingleNode(xUrlListener, nsManager).SetValue(LISTENER_URL);
 
    // create webrequest for message transmission
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(cmbDeviceUrl.Text);
    request.Method = "POST";
    request.ContentType = "application/vnd.cip4-jmf+xml";
    doc.Save(request.GetRequestStream());
 
    // send and close response
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    response.Close();
}

A new XMLDocument object will be created which loads the JMF message template. Using the XPathNavigator the XML-Default-Namespace will be extend by a prefix. The parameter {LISTENER_URL} will be set via an XPath expression. Finally the JMF-Command will be packed into an HttpWebRequest for transmission. The request attributes “Method” and “ContentType” has to be set to “POST” and “application/vnd.cip4-jmf+xml”.

Extract details from a JMF-Status-Message using XPath

This C# Code-Snippet demonstrates how to extract details out of a JMF-Status-Message. For data extraction open XML-File stream in read-only mode is recommended. So the XPathNavigator will be created by the XPathDocument. After adjustment of the XML-Default-Namespace all attributes needed can be read using XPath expressions:

// additional namespaces needed
using System.Xml;
using System.Xml.XPath;
 
/// <summary>
/// Extracts details of received JMF Messages by using of XPath.
/// </summary>
/// <param name="ctx">Http Context which contains the message.</param>
private void JMFHandler(HttpListenerContext ctx)
{
    // parse message stream.
    XPathDocument doc = new XPathDocument(ctx.Request.InputStream);
    XPathNavigator nav = doc.CreateNavigator();
 
    // ATTENTION: Take care for the XML Default Namespace!
    // In order to use XPath the creation of a prefix (e. g. "ns") is required.
    XmlNamespaceManager nsManager = new XmlNamespaceManager(nav.NameTable);
    nsManager.AddNamespace("ns", "http://www.CIP4.org/JDFSchema_1_1");
 
    // extract message details (prefix "ns") and display.
    string xDeviceId = "/ns:JMF/ns:Signal/ns:DeviceInfo/@DeviceID"; // DeviceId
    string deviceId = nav.SelectSingleNode(xDeviceId, nsManager).Value;
 
    string xDeviceStatus = "/ns:JMF/ns:Signal/ns:DeviceInfo/@DeviceStatus"; // DeviceStatus
    string deviceStatus = nav.SelectSingleNode(xDeviceStatus, nsManager).Value;
 
    string xDeviceOperationMode = "/ns:JMF/ns:Signal/ns:DeviceInfo/@DeviceOperationMode"; // DeviceOperationMode
    string operationMode = nav.SelectSingleNode(xDeviceOperationMode, nsManager).Value;
 
    string xTimeStamp = "/ns:JMF/@TimeStamp"; // TimeStamp attribute
    string timeStamp = nav.SelectSingleNode(xTimeStamp, nsManager).Value;
 
    // display message details
    DisplayMsgDetails(deviceId, deviceStatus, operationMode, timeStamp, nav.OuterXml);
}