Table of Contents

Creating a connection handler script for an IP Matrix element

A connection handler script is part of the MediaOps Live mediation layer and is responsible for detecting and managing connections between endpoints for a specific connector (for example, a broadcast controller). This tutorial walks you through the process of creating your own connection handler.

Expected duration: 60 minutes

Tip

For details about connection handler scripts and how they are used in MediaOps Live, refer to Connection handler scripts.

Prerequisites

Overview

Step 1: Create a new automation script

Create a new automation script in Visual Studio, using DIS. For details, refer to the DIS documentation.

Make sure the following requirements are met, so that the mediation layer will recognize your script as a connection handler script:

  • The script name should end with _ConnectionHandler, e.g., IPMatrix_ConnectionHandler.
  • The script must be included in the folder MediaOps/ConnectionHandlerScripts.
  • The following two input parameters must be present:
    • Action (string)
    • Input Data (string)

Step 2: Add NuGet packages and using statements

  1. Add the following NuGet packages to your project:

    • Skyline.DataMiner.Dev.Utils.Solutions.MediaOps.Live
    • Skyline.DataMiner.Dev.Utils.Solutions.MediaOps.Live.Automation
    • Skyline.DataMiner.Dev.Automation
    • Skyline.DataMiner.Core.DataMinerSystem.Automation
  2. Add the following using statements to your script:

    using Skyline.DataMiner.Solutions.MediaOps.Live.API;
    using Skyline.DataMiner.Solutions.MediaOps.Live.Automation;
    

Step 3: Make the script inherit from the ConnectionHandlerScript base class

Next, you need to make the script inherit from the ConnectionHandlerScript base class. The Run method is no longer needed, as the base class already implements it, so you can remove it. The base class will call the appropriate methods that you will implement in the next steps.

using Skyline.DataMiner.Solutions.MediaOps.Live.Automation.Mediation.ConnectionHandlers;

public class Script : ConnectionHandlerScript
{

}

Alternatively, you can also keep the Script class, and create a new class that inherits from ConnectionHandler. In some situations, this could be more flexible, for example when you want to dynamically call different connection handlers based on the input data.

using Skyline.DataMiner.Solutions.MediaOps.Live.Automation.Mediation.ConnectionHandlers;

public class Script
{
    public void Run(IEngine engine)
    {
        var handler = new Generic_Matrix_ConnectionHandler();
        handler.Execute(engine);
    }
}

public class Generic_Matrix_ConnectionHandler : ConnectionHandler
{

}

Step 4: Implement GetSupportedElements and GetSubscriptionInfo

Now you can implement the required methods in your script. Each serves a different purpose when interacting with the mediation layer.

The first method to implement is GetSupportedElements. This method should return a list of elements that are supported by this connection handler script. The base class provides a list of elements that run on the same hosting DMA as the script. This list should be used to build the list of supported elements.

In this case, the script has to support all elements that use the Generic Dynamic Table protocol and that have a name starting with Decoder . There is no need to subscribe to the encoder elements, as they are only used as a source for the connection and their configuration is static.

public override IEnumerable<ElementInfo> GetSupportedElements(IEngine engine, IEnumerable<ElementInfo> elements)
{
    return elements.Where(e => e.Protocol == "Generic Dynamic Table");
}

The next method to implement is GetSubscriptionInfo. This method tells the mediation layer which parameters to subscribe to on the supported elements. Both standalone and table parameters are supported. When a parameter changes, the ProcessParameterUpdate method will be called with the new parameter value.

In this case, the Entries table (ID 200) on the decoder elements should be subscribed to.

public override IEnumerable<SubscriptionInfo> GetSubscriptionInfo(IEngine engine)
{
    return new[]
    {
        new SubscriptionInfo(SubscriptionInfo.ParameterType.Table, 200), // Entries Table
    };
}

Step 5: Implement ProcessParameterUpdate

The ProcessParameterUpdate method is called whenever a subscribed parameter changes on one of the supported elements. Use these updates to connect or disconnect endpoints through the API.

This method should implement the following workflow:

  • Inspect the updated and/or deleted rows.
  • Find matching source and destination endpoints using connectionEngine.Api.
  • Register new connections or disconnections using connectionEngine.RegisterConnections().

In this case, there should be a check if the IP In row was updated. If yes, the script should check if a multicast IP is configured, and if a multicast IP is configured, it should try to find the corresponding source endpoint using the multicast IP. Optionally, you could also check if the source IP and port match, but in this case the multicast IP is available.

You can find the destination endpoint using the element ID, as there is only one destination endpoint per decoder element.

Note

The destination endpoint is always mandatory. However, it could be that the corresponding source endpoint is not found. In that case it is still possible to register the connection, but with an unknown source endpoint. This could happen when the source endpoint is not (yet) provisioned in MediaOps.

public override void ProcessParameterUpdate(IEngine engine, IConnectionHandlerEngine connectionEngine, ParameterUpdate update)
{
    if (update.ParameterId != 200)
    {
        // we are only interested in updates of the outputs table
        return;
    }

    var updatedConnections = new List<ConnectionUpdate>();

    var elementId = update.DmsElementId;
    var destinationEndpoint = connectionEngine.Api.Endpoints.GetByElement(elementId)
        .SingleOrDefault(e => e.Role == Role.Destination);

    if (destinationEndpoint == null)
    {
        throw new InvalidOperationException($"Element with ID {elementId} does not have a destination endpoint.");
    }

    if (update.UpdatedRows != null)
    {
        var row = update.UpdatedRows.Values.FirstOrDefault(x => String.Equals(x[1], "IP In"));
        if (row == null)
        {
            // No 'IP In' row found in the updated rows.
            return;
        }

        var multicastIp = Convert.ToString(row[4]);
        var isConnected = !String.IsNullOrWhiteSpace(multicastIp);

        if (isConnected)
        {
            var multicast = new Multicast(multicastIp);
            var sourceEndpoint = connectionEngine.Api.Endpoints.GetByMulticasts(new[] { multicast })
                .SingleOrDefault();

            if (sourceEndpoint != null)
            {
                // register connection between source and destination
                updatedConnections.Add(new ConnectionUpdate(sourceEndpoint, destinationEndpoint));
            }
            else
            {
                // source endpoint not found, register as connected to an unknown source
                updatedConnections.Add(new ConnectionUpdate(destinationEndpoint, isConnected: true));
            }
        }
        else
        {
            // register destination as disconnected
            updatedConnections.Add(new ConnectionUpdate(destinationEndpoint, isConnected: false));
        }
    }

    if (update.DeletedRows != null)
    {
        // not implemented for this example
    }

    if (updatedConnections.Count > 0)
    {
        connectionEngine.RegisterConnections(updatedConnections);
    }
}

Step 6: Implement Connect and Disconnect

Finally, you need to implement the Connect and Disconnect methods. These methods are called when a connect or disconnect request is made through the API or the Control Surface app.

With these methods, the script will perform the necessary sets on the device element (in this case the decoder) to create or remove connections. This typically involves setting (table) parameters or sending inter-app messages.

Note

The mediation layer can provide requests in bulk; however, in this example they are processed one by one. When the device connector supports bulk operations, using them is recommended for better performance. The example code below shows how to group the requests by element, so that you can perform a single bulk operation per element.

public override void Connect(IEngine engine, IConnectionHandlerEngine connectionEngine, CreateConnectionsRequest createConnectionsRequest)
{
    var groupedByDestinationElement = createConnectionsRequest.Connections.GroupBy(x => x.DestinationEndpoint.Element);

    foreach (var group in groupedByDestinationElement)
    {
        var elementId = group.Key.Value;
        var element = engine.FindElement(elementId.AgentId, elementId.ElementId);

        foreach (var connection in group)
        {
            var endpoint = connection.SourceEndpoint;
            var multicastIP = endpoint.TransportTypeTSoIP.MulticastIP;

            // Connect by setting the multicast IP in the "IP In" row of the Entries table
            element.SetParameter("Text Value (Entries)", "IP In", multicastIP);
        }
    }
}

public override void Disconnect(IEngine engine, IConnectionHandlerEngine connectionEngine, DisconnectDestinationsRequest disconnectDestinationsRequest)
{
    var groupedByDestinationElement = disconnectDestinationsRequest.Destinations.GroupBy(x => x.Element);

    foreach (var group in groupedByDestinationElement)
    {
        var elementId = group.Key.Value;
        var element = engine.FindElement(elementId.AgentId, elementId.ElementId);

        foreach (var destination in group)
        {
            // Disconnect by clearing the multicast IP
            element.SetParameter("Text Value (Entries)", "IP In", String.Empty);
        }
    }
}

Step 7: Publish and test the script

  1. Now that all the necessary code has been implemented, build the project to make sure there are no errors.

  2. If the build is successful, publish the script to the DMA using DIS.

  3. In DataMiner Cube, go to the Connection Handler Scripts page of the MediaOps Mediation element.

    On this page, you should see that the connection handler script is listed as an available script.

    The decoder elements should automatically be linked to this script. If needed, you can link them manually by selecting the script in the Mediated Elements Table on the General page of the MediaOps Mediation element.

  4. Test your connection handler script by making some connections in the Control Surface app.

    You can verify connections by looking at the button of the destination endpoint. The button should show the name of the connected source endpoint. You can also check the device element directly.