.NET Data Processing extensions

Constructor

The constructor is required, and accepts the system, user and password. The user and password are credentials mapped in the OmniFi Administration application.

public class MyDotNetImportExtensions : ACustomImportExtension
{
    public MyDotNetImportExtensions(string System, string UserId, string Password)
            : base(System, UserId, Password)
    {
    }
}

SetOptionsImpl

If the extension has any custom options, they are read here. In the example below the extension has the option “ExtensionOption”:

605
public override void SetOptionsImpl(List<Plugin_Argument> Options)
{
    var unsupportedOptions = Options
        .Where((x) => x.ParameterName != "OutputFile")
        .Select((x) => x.ParameterName)
        .ToArray();
    if (unsupportedOptions.Length > 0)
        throw new ApplicationException(
            "Options not supported: " 
            + String.Join(", ", unsupportedOptions));
 
    string outputFile = Options
        .FirstOrDefault((x) => x.ParameterName == "OutputFile")
        ?.StringValue;
 
    if (!String.IsNullOrEmpty(outputFile))
        this._file = outputFile;
            
    var directory = Path.GetDirectoryName(this._file);
    if (!Directory.Exists(directory))
        Directory.CreateDirectory(directory);
}

GetFunctionsImpl

Gets a list of functions supported by the extension, as a list of strings.

public override List<string> GetFunctionsImpl()
{
    return (new List<string> { "DumpToFile" });
}

GetFunctionParametersImpl

Defines function parameters for the specified function. Returns a list of Plugin_Parameter.

public override List<Plugin_Parameter> GetFunctionParametersImpl(string Function)
{
    if (Function == null)
    {
        throw new ArgumentNullException(nameof(Function));
    }
 
    if (Function != "DumpToFile")
        throw new ApplicationException($"Function '{Function}' not supported");
 
    List<Plugin_Parameter> parameters = new List<Plugin_Parameter>
    {
        new Plugin_Parameter {
            Type = Plugin_DataType.INTEGER,
            Name = "intParam_0",
            Description = "Integer Parameter 0",
            Mandatory = true,
            Comment = "Provide an integer number"
        },
        new Plugin_Parameter {
            Type = Plugin_DataType.STRING,
            Name = "strParam_0",
            Description = "String Parameter 0",
            Mandatory = false,
            Comment = "Provide string, or don't"
        },
        new Plugin_Parameter {
            Type = Plugin_DataType.DOUBLE,
            Name = "dblParam_0",
            Description = "Double Parameter 0",
            Mandatory = true,
            Comment = "Provide a double-precision number"
        },
    };
    return (parameters);
}

GetFunctionMetadataImpl

Defines return values for the specified function, returning a List<Metadata>.

public override List<Plugin_Metadata> GetFunctionMetadataImpl(string Function)
{
  return new List<Plugin_Metadata>
  {
    new Plugin_Metadata
    {
      Type = Plugin_DataType.STRING,
      Name = "str",
      Description = "String"
      },
    new Plugin_Metadata
    {
      Type = Plugin_DataType.INTEGER,
      Name = "num",
      Description = "Integer"
      }
  };
}

The metadata is available to the user using the GetValues Data Processing function.

ExecuteFunctionImpl

Executes the specified function with the supplied arguments. Arguments are supplied as a List<Plugin_Argument>. The type of arguments is defined by the GetFunctionParameters() method.
The method returns a List<Plugin_ArrayArguments> corresponding to the function metadata implemented by GetFunctionMetadataImpl().
The return of a record (of type List<Plugin_Argument>) from the method signals a successful execution. Any exception that is not handled by the method will fail the row and the error message is presented to the user.

public override List<Plugin_ArrayArgument> ExecuteFunctionImpl(
    string Function,
    List<Plugin_Argument> Parameters)
{
    Logger.Debug("ExecuteFunctionImpl(): Enter");
    if (Function == null)
    {
        throw new ArgumentNullException(nameof(Function));
    }
 
    if (Function != "DumpToFile")
    {
        throw new ApplicationException($"Function '{Function}' not supported");
    }
 
    var paramLookup = Parameters.ToDictionary(
        (x) => x.ParameterName
    );
    Plugin_Argument intValue, dblValue, strValue;
 
    Logger.Debug("ExecuteFunctionImpl(): Checking Integer Parameter 0");
    if (!paramLookup.TryGetValue("intParam_0", out intValue) || intValue.IntegerValue == null)
        throw new ApplicationException("Mandatory argument 'Integer Parameter 0' not supplied");
    int iParam = (int)intValue.IntegerValue;
 
    Logger.Debug("ExecuteFunctionImpl(): Checking Double Parameter 0");
    if (!paramLookup.TryGetValue("dblParam_0", out dblValue) || dblValue.DoubleValue == null)
        throw new ApplicationException("Mandatory argument 'Double Parameter 0' not supplied");
    double dParam = (double)dblValue.DoubleValue;
 
    Logger.Debug("ExecuteFunctionImpl(): Obtaining String Parameter 0");
    string sParam;
    if (!paramLookup.TryGetValue("strParam_0", out strValue) || strValue.StringValue == null)
    {
        Logger.Warning(
            "ExecuteFunctionImpl(): String Parameter 0 not supplied\nreverting to default.");
        sParam = String.Empty;
    }
    else
    {
        sParam = strValue.StringValue;
    }
    string text = $"L{this._line};{iParam};{dParam};{sParam}";
 
 
    File.AppendAllLines(this._file, new string[] { text });
 
    List<Plugin_ArrayArgument> result = new List<Plugin_ArrayArgument>
    {
        new Plugin_ArrayArgument ("number", new int?[] { this._line }),
        new Plugin_ArrayArgument ("formatted_text", new string[] { text }),
    };
    this._line++;
    Logger.Debug("ExecuteFunctionImpl(): Return");
    return (result);
}

ShutdownImpl

The Shutdown() method is called just before the extension is closed, allowing you to clean up any cached state. Note that the plugin may be shut down not only because the import is completed, but also because the user has configured a batch size, and the current batch has been filled.

🚧

Extension life-time

Data Processing extensions are created by the service header and disposed with the next service header or when the batch size is reached. Long running operations, like connecting to a database, are best performed once in the constructor and reused throughout the execution. For optimal performance you can accumulate execution instructions throughout the lifetime and batch-commit in the Shutdown() method.

However, since the lifetime of the plugin is not guaranteed you cannot assume e.g. a running average is correct, since a header may be split up into several batches with restarts of the extension in between.