Skip to content

Latest commit

 

History

History
303 lines (269 loc) · 13 KB

dynamic-links.md

File metadata and controls

303 lines (269 loc) · 13 KB

Dynamic Links

Simple Dynamic Link

By default a DynamicLink is made as Read only unless you specify different mode

    ...
    // Get the element where the dynamic link has to be added
    var myObj = Owner.Get<Motor>("Motor1");
    // Reach the specific variable
    var speedValue = Owner.GetObject("SpeedLabel").GetVariable("Text");
    // Create the dynamic link and specify the mode (optional - read only if empty)
    myObj.SpeedVariable.SetDynamicLink(speedValue, DynamicLinkMode.Read);
    ...

Dynamic link manipulation

Warning

Manipulating a dynamic link is a very advanced topic and should be used with caution. It is recommended to use the default dynamic link APIs unless you have a specific use case.

Resolve a DynamicLink

    ...
    // Get the element containing a DynamicLink
    TrendPen originalPen = InformationModel.Get<TrendPen>(Owner.GetAlias("AliasPen").NodeId);
    // Read the variable value
    var myVariable = (String)originalPen.FindVariable("DynamicLink").Value;
    // Resolve the dynamic link target
    var result = LogicObject.Context.ResolvePath(myVariable);
    var resolvedNode = result.ResolvedNode;
    // Set the target variable somewhere in the project
    Owner.Find<ComboBox>("ComboBox1").SelectedItem = resolvedNode.Owner.NodeId;
    ...
    ...
    // Get the element containing a DynamicLink
    var textVariable = Owner.Children.Get<IUAVariable>("Text");
    // Get the DynamicLink node
    var dynamicLinkNode = textVariable.Children.Get("DynamicLink");
    // Get the pointed node
    var pointedNode = dynamicLinkNode.Refs.GetNodes(FTOptix.Core.ReferenceTypes.Resolves).First();
    ...

Check for a DynamicLink

// Check if the current node has a dynamic link
var dynamicLink = inputVariable.Refs.GetVariable(FTOptix.CoreBase.ReferenceTypes.HasDynamicLink);
// Retrieve the path of the dynamic link
var dynamicLinkPath = (string)dynamicLink.Value;
// Resolve the dynamic link and get to the target node
var targetVariable = LogicObject.Context.ResolvePath(inputVariable, dynamicLinkPath).ResolvedNode;

Check for broken DynamicLink

[ExportMethod]
public void GetBrokenDynamicLinks()
{
    var nodesWithBrokenDynamicLink = new List<string>();
    var projectNodes = Project.Current.Parent.FindNodesByType<IUANode>();
 
    foreach (var n in projectNodes)
    {
        // Check if the current node has a dynamic link
        var dynamicLink = n.Refs.GetVariable(FTOptix.CoreBase.ReferenceTypes.HasDynamicLink);
        if (dynamicLink == null) continue;
        // Retrieve the path of the dynamic link
        var dynamicLinkPath = (string)dynamicLink.Value;
        // Resolve the dynamic link and get to the target node
        var targetVariable = LogicObject.Context.ResolvePath(n, dynamicLinkPath).ResolvedNode;
            if (targetVariable == null)
        {
            nodesWithBrokenDynamicLink.Add("Node BrowsePath: " + Log.Node(n));
        }
    }
 
    foreach (var info in nodesWithBrokenDynamicLink)
    {
        Log.Info(info);
    }
}

Change the EU Mode of a DynamicLink

[ExportMethod]
public void SetEuModeOfText()
{
    // Get to the label that will display the value of the variable
    var myLabelText = Owner.GetVariable("Label1/Text");
    // Get the dynamic link at the text property
    var dynamicLink = myLabelText.Refs.GetVariable(FTOptix.CoreBase.ReferenceTypes.HasDynamicLink);
    // Set the EU mode of the dynamic link to "Localize"
    ((DynamicLink)dynamicLink).EUMode = DynamicLinkEUMode.SetParentLocalizedEngineeringUnit;
    // Set the EU mode of the dynamic link to "Set Parent as Source" (no conversion)
    //((DynamicLink)dynamicLink).EUMode = DynamicLinkEUMode.SetParentEngineeringUnitAsSource;
}

Dynamic Link to a bit indexed word

[ExportMethod]
public void AddDynamicLinkToBitOfIntegerVariable()
{
    // Get the variable
    var myBitAlarm = Project.Current.GetVariable("Model/BitVariable");
    // Create a fake variable
    IUAVariable tempVariable = null;
    // Add a dynamic link to the fake variable (to trigger the link mechanism)
    myBitAlarm.SetDynamicLink(tempVariable, DynamicLinkMode.ReadWrite);
    // Replace the dynamic link content to the real target value
    myBitAlarm.GetVariable("DynamicLink").Value = "../Int32Variable.1";
}

Dynamic Link to a single element of array variable

[ExportMethod]
public void StuffDynamicLinkToArrayElement(IUAVariable sourceVariable, IUAVariable targetDynamicLink, uint sourceVariableArrayIndex, int targetDynamicLinkArrayIndex, DynamicLinkMode dynamicLinkMode)
{
    // Prepare the BrowseName for the dynamic link variable
    string dynamicLinkVariableBrowseName = $"DynamicLink_{sourceVariableArrayIndex}";
    // Create the dynamic link object
    DynamicLink newDynamicLink = InformationModel.MakeVariable<DynamicLink>(dynamicLinkVariableBrowseName, FTOptix.Core.DataTypes.NodePath);
    // Set the dynamic link values
    newDynamicLink.Value = DynamicLinkPath.MakePath(sourceVariable, targetDynamicLink);
    newDynamicLink.Mode = dynamicLinkMode;
    newDynamicLink.ParentArrayIndexVariable.Value = sourceVariableArrayIndex;
    // Check if the target of dynamic link is an array
    if (targetDynamicLinkArrayIndex >= 0)
    {
        //If it is set the index by adding square brackets with inside the index value at the end of the path
        newDynamicLink.Value = $"{newDynamicLink.Value.Value}[{targetDynamicLinkArrayIndex}]";
    }
    // Add the dynamic link reference to the dynamic link (OPCUA specs)
    sourceVariable.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasDynamicLink, newDynamicLink);
    // Set the proper modelling rule (OPCUA specs)
    newDynamicLink.SetModellingRuleRecursive();
}

Formatted dynamic link

A formatted dynamic link is very similar to a string formatter and it is used to dynamically reference different objects in the project, such as using the same widget to control different motors using a SpinBox

public void StuffCreateNewDynamicLinkFormatter()
{
    // Get to the text box where the dynamic link has to be created
    IUAVariable targetVariable = Owner.Get("TextBox1").GetVariable("Text");
    // Remove dynamic link (if any)
    targetVariable.ResetDynamicLink();
    // Create a new empty dynamic link object
    DynamicLink newDynamicLink = InformationModel.MakeVariable<DynamicLink>("DynamicLink", FTOptix.Core.DataTypes.NodePath);
    newDynamicLink.Value = "";
    // Create a string formatter to be added to the dynamic link
    StringFormatter newStringFormatter = InformationModel.MakeObject<StringFormatter>("DynamicLinkFormatter", FTOptix.CoreBase.ObjectTypes.StringFormatter);
    // Set the proper format for the string formatter, where curly brackets are used as placeholders
    newStringFormatter.Format = "/Objects/StuffTest/Model/Folder1/Variable5[{0},{1}]";
    // Set the sources for the placeholders
    IUAVariable source0 = InformationModel.MakeVariable("Source0", OpcUa.DataTypes.BaseDataType);
    IUAVariable source1 = InformationModel.MakeVariable("Source1", OpcUa.DataTypes.BaseDataType);
    source0.SetDynamicLink(Project.Current.GetVariable("Model/Folder1/Variable1"));
    source1.SetDynamicLink(Project.Current.GetVariable("Model/Folder1/Variable2"));
    // Add the references about the children variables to the string formatter
    newStringFormatter.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source0);
    newStringFormatter.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source1);
    // Set the dynamic link mode (if needed)
    newDynamicLink.Mode = DynamicLinkMode.ReadWrite;
    // Set the proper references and modelling rules (OPCUA specs)
    newDynamicLink.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasConverter, newStringFormatter);
    newStringFormatter.SetModellingRuleRecursive();
    targetVariable.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasDynamicLink, newDynamicLink);
    newDynamicLink.SetModellingRuleRecursive();       
}

Advanced dynamic links

Warning

Advanced dynamic links are a very advanced topic and should be used with caution. It is recommended to use the default dynamic link APIs unless you have a specific use case.

Creating a String Formatter

// Create a new variable
var variable4 = InformationModel.MakeVariable("Variable4", OpcUa.DataTypes.String); 
// Add the variable to the Model folder
Project.Current.Get("Model").Add(variable4); 
// Create a string formatter
var stringFormatter = InformationModel.Make<StringFormatter>("StringFormatter1"); 
// Set the base text (with placeholders)
stringFormatter.Format = "{0} and {1}"; 
// Configure placeholders
var source0 = InformationModel.MakeVariable("Source0", OpcUa.DataTypes.BaseDataType); 
stringFormatter.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source0); 
var source1 = InformationModel.MakeVariable("Source1", OpcUa.DataTypes.BaseDataType); 
stringFormatter.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source1); 
// Add the new converter to the variable
variable4.SetConverter(stringFormatter);
// Set the targets for the dynamic links 
var variable1 = Project.Current.GetVariable("Model/Variable1"); 
var variable2 = Project.Current.GetVariable("Model/Variable2"); 
source0.SetDynamicLink(variable1); 
source1.SetDynamicLink(variable2);

Creating an Expression Evaluator

[ExportMethod]
public void Method1()
{
    // Get the source variables
    var var1 = Project.Current.GetVariable("Model/Variable1");
    var var2 = Project.Current.GetVariable("Model/Variable2");
    var var3 = Project.Current.GetVariable("Model/Variable3");
    // Create the expression evaluator object
    var expressionEvaluator = InformationModel.MakeObject<ExpressionEvaluator>("ExpressionEvaluator");
    // Set the format
    expressionEvaluator.Expression = "{0} + {1}";
    // Set the dynamic links to the source variables
    var source0 = InformationModel.MakeVariable("Source0", OpcUa.DataTypes.BaseDataType);
    source0.SetDynamicLink(var2);
    var source1 = InformationModel.MakeVariable("Source1", OpcUa.DataTypes.BaseDataType);
    source1.SetDynamicLink(var3);
    // Add the references (OPCUA specs)
    expressionEvaluator.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source0);
    expressionEvaluator.Refs.AddReference(FTOptix.CoreBase.ReferenceTypes.HasSource, source1);
    // Add the converter to the variable
    var1.SetConverter(expressionEvaluator);
}

Creating a Key-Value Converter

private void StuffCreateKeyPair(IUAVariable targetNode, IUAVariable sourceVariable)
{
    // Create the new key value converter
    ValueMapConverter newValueMapConverter = InformationModel.MakeObject<ValueMapConverter>("KeyValueConverter1", FTOptix.CoreBase.ObjectTypes.ValueMapConverter);
    // Create the pairs object
    IUAObject newPairs = InformationModel.MakeObject(new QualifiedName(FTOptix.CoreBase.ObjectTypes.ValueMapConverter.NamespaceIndex, "Pairs"));
    // For each pair, set properties
    for (int i = 0; i < 3; i++)
    {
        // First element is the default one
        string pairBrowseName = "Pair";
        if (i > 0)
            pairBrowseName += i.ToString();
        // Create the new pair
        IUAObject newPair = InformationModel.MakeObject(pairBrowseName, FTOptix.CoreBase.ObjectTypes.ValueMapPair);
        IUAVariable newKey = newPair.GetVariable("Key");
        IUAVariable newValue = newPair.GetVariable("Value");
        // Set the proper datatype
        newKey.DataType = OpcUa.DataTypes.UInt32;
        newValue.DataType = FTOptix.Core.DataTypes.Color;
        // Set the proper value
        newKey.Value = i;
        newValue.Value = System.Drawing.Color.Violet.ToArgb();
        // Add the pair to the map
        newPairs.Add(newPair);
    }
    // Add the map to the key value converter
    newValueMapConverter.Add(newPairs);
    newValueMapConverter.SourceVariable.SetDynamicLink(sourceVariable);
    // Add the key value converter to the variable
    targetNode.SetConverter(newValueMapConverter);
}

Creating a Conditional Converter

private void StuffCreateConditionalConverter(IUAVariable targetNode, IUAVariable sourceVariable)
{
    // Create the conditional converter
    ConditionalConverter newConditionalConverter = InformationModel.MakeObject<ConditionalConverter>("ConditionalConverter1", FTOptix.CoreBase.ObjectTypes.ConditionalConverter);
    // Set the "false" condition
    newConditionalConverter.FalseValueVariable.DataType = FTOptix.Core.DataTypes.Color;
    newConditionalConverter.FalseValueVariable.Value = System.Drawing.Color.Red.ToArgb();
    // Set the "true" condition
    newConditionalConverter.TrueValueVariable.DataType = FTOptix.Core.DataTypes.Color;
    newConditionalConverter.TrueValueVariable.Value = System.Drawing.Color.Green.ToArgb();
    // Add the source variable
    newConditionalConverter.ConditionVariable.SetDynamicLink(sourceVariable);
    // Set the dynamic link to the object
    newConditionalConverter.Mode = DynamicLinkMode.Read;
    targetNode.SetConverter(newConditionalConverter);
}