Creating a connection handler script for a Generic 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
- Range 1.0.1.x of the Generic Matrix connector is installed on the DMA, and an element has been created using this connector.
- MediaOps Live is installed on the DMA.
- Visual Studio is installed on your machine.
- The DIS extension is installed in Visual Studio.
- Some endpoints and virtual signal groups have been created in MediaOps, for example via the tutorial Manually provisioning endpoints and virtual signal groups for a Generic Matrix element.
Overview
- Step 1: Create a new automation script
- Step 2: Add NuGet packages and using statements
- Step 3: Make the script inherit from the ConnectionHandlerScript base class
- Step 4: Implement GetSupportedElements and GetSubscriptionInfo
- Step 5: Implement ProcessParameterUpdate
- Step 6: Implement Connect and Disconnect
- Step 7: Publish and test the script
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.,Generic_Matrix_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
Add the following NuGet packages to your project:
Skyline.DataMiner.Dev.Utils.Solutions.MediaOps.LiveSkyline.DataMiner.Dev.Utils.Solutions.MediaOps.Live.AutomationSkyline.DataMiner.Dev.AutomationSkyline.DataMiner.Core.DataMinerSystem.Automation
Add the following
usingstatements 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.
public override IEnumerable<ElementInfo> GetSupportedElements(IEngine engine, IEnumerable<ElementInfo> elements)
{
return elements.Where(e => e.Protocol == "Generic Matrix");
}
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.
public override IEnumerable<SubscriptionInfo> GetSubscriptionInfo(IEngine engine)
{
return new[]
{
new SubscriptionInfo(SubscriptionInfo.ParameterType.Table, 1100), // Outputs 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().
The example below will look up the source and destination endpoints based on the element ID and the identifier from the table. In a similar way, you could also use other properties, such as a multicast IP address.
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 != 1100)
{
// we are only interested in updates of the outputs table
return;
}
var updatedConnections = new List<ConnectionUpdate>();
var elementId = update.DmsElementId;
// Handle updated rows
if (update.UpdatedRows != null)
{
foreach (var row in update.UpdatedRows.Values)
{
var outputIdentifier = Convert.ToString(row[0]);
var inputIdentifier = Convert.ToString(row[5]);
var output = connectionEngine.Api.Endpoints.GetByRoleElementAndIdentifier(Role.Destination, elementId, outputIdentifier)
?? throw new InvalidOperationException($"Destination endpoint '{outputIdentifier}' not found for element '{elementId}'.");
if (String.IsNullOrWhiteSpace(inputIdentifier))
{
// Connected input is empty, which means that the output is disconnected.
updatedConnections.Add(new ConnectionUpdate(output, isConnected: false));
continue;
}
var input = connectionEngine.Api.Endpoints.GetByRoleElementAndIdentifier(Role.Source, elementId, inputIdentifier);
if (input != null)
{
updatedConnections.Add(new ConnectionUpdate(input, output));
}
else
{
updatedConnections.Add(new ConnectionUpdate(output, isConnected: true));
}
}
}
// Handle deleted rows
if (update.DeletedRows != null)
{
foreach (var row in update.DeletedRows.Values)
{
var outputIdentifier = Convert.ToString(row[0]);
var output = connectionEngine.Api.Endpoints.GetByRoleElementAndIdentifier(Role.Destination, elementId, outputIdentifier)
?? throw new InvalidOperationException($"Destination endpoint '{outputIdentifier}' not found for element '{elementId}'.");
updatedConnections.Add(new ConnectionUpdate(output, isConnected: false));
}
}
// Register the connections with the mediation layer.
// This will update, for instance, the Control Surface application.
if (updatedConnections.Count > 0)
{
connectionEngine.RegisterConnections(updatedConnections);
}
}
Step 6: Implement Connect and Disconnect
Finally, you need to implement the Connect and Disconnect methods.
With these methods, the script will perform the necessary sets on the device element 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 rowKey = connection.DestinationEndpoint.Identifier;
var sourceIdentifier = connection.SourceEndpoint?.Identifier ?? String.Empty;
element.SetParameterByPrimaryKey("Connected Input (Router Control Outputs)", rowKey, sourceIdentifier);
}
}
}
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)
{
var rowKey = destination.Identifier;
element.SetParameterByPrimaryKey("Connected Input (Router Control Outputs)", rowKey, String.Empty);
}
}
}
Step 7: Publish and test the script
Now that all the necessary code has been implemented, build the project to make sure there are no errors.
If the build is successful, publish the script to the DMA using DIS.
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.
If there is a Generic Matrix element on the same DMA, it should automatically be linked to this script. If needed, you can link it manually by selecting it in the Mediated Elements Table on the General page of the MediaOps Mediation element.
Test your script by creating a connection:
In the Control Surface app, select a source and destination virtual signal group.
Click the Connect button.
A connection should now be created between the source and destination. You can verify connections by looking at the button of the destination endpoint. The button should show the name of the connected source endpoint. Note that you can also check the device element directly in Cube.
Tip
The SLC-AS-MediaOps.LIVE-Tutorial-GenericMatrix repository on GitHub contains the complete script that you can use as a reference.