Search

Home

Home

USER OPERATOR

USER OPERATOR

The USER OPERATOR allows you to create a custom data-processing algorithm that operates on the input data bus. This gives you full control over the operation logic—from simple transformations to advanced conditional processing or multi-step operations.

The operator’s logic is defined using C# code that you declare directly in the operator configuration. Within the code, you have access to:

  • the full data stream from the input data bus,
  • project parameters,
  • global variables,
  • the ability to define a new output data stream, if needed.

The code is automatically compiled when the GRAVITY application is launched, and the resulting operator functions on par with built-in operators.

Typical use cases include:

  • implementing non-standard business logic,
  • handling complex data or data requiring conditional processing,
  • integration with external libraries or services.

The USER OPERATOR is a tool for advanced users who require flexibility beyond the standard set of operators provided by GRAVITY.

An example of a project using the USER OPERATOR:

image

OPERATOR CONFIGURATION

Creating this operator requires at least a basic knowledge of C# as the operator configuration mainly consists of defining the input data processing algorithm in C#.

As a first step, you need to decide on the basic data processing parameters (in the User operator properties tab), such as:

  • Target result → whether the operator should create a completely new data bus (Create busbar from the result) or modify the values of bits in an existing data bus (Value into an existing column).
image

When you choose the option to create a new data bus, the Columns tab will appear, where you need to define the output data bus bits so that they match the properties of the objects returned by the algorithm.

When choosing to modify existing bits, return the modified input data bus objects in the C# code.

  • Code part → specifies whether defining the algorithm requires a full class responsible for the operator’s behavior (Classes), or whether calling a single method executed for each data bus record is sufficient (Main method only).
image

When you select the Classes option, you gain greater control over the code. You can create the entire structure of the operator class yourself and define additional custom classes.

Selecting the Main method only option is recommended when you want to perform simple calculation operations and can implement them within a single function.

  • Main class name → the name of the operator class. The system suggests an automatically generated name to ensure uniqueness within the project.
  • Additional use → additional using declarations, for example: System.Timers.
  • Add additional declarations without the using, keyword, separated by a space or a new line.

    By default, the following using declarations are available:

    using System;
    using System.Xml;
    using System.Xml.Linq;
    using System.Data;
    using System.Linq;
    using System.Collections;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Forms;
    using System.Data.Common;
    using System.Text;
    using Newtonsoft.Json.Linq;
    using MongoDB.Bson;
  • Additional References → specifies whether you need additional libraries. You can use the Browse button to locate a library on disk or enter the full path manually. Remember to also add the appropriate using declarations for the selected library.
image

Use the Generate code template button to generate the operator’s code template (on the Code tab).

OPERATOR CODE (C#)

When creating the operator code, depending on the Code part setting, the system automatically generates the operator class definition (after you click Generate code template) and adds the function code and the definition of the class defined in the operator.

icon
You can freely modify the code generated by the system (add functions, classes, variables, etc.), but keep in mind the following requirements: When Code part = Classes, the class name in the code must match the class name in the Main class name field, and the class must include a method with the following signature: List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters) When Code part = Main method only, a method with the following signature must exist: dynamic UserOperationForRecord(dynamic record, Hashtable parameters)

The system uses the dynamic data type to represent an object that is a data bus record. This is due to differences between the classes representing individual data buses and the need to maintain the universality of the defined algorithms.

Classes representing data bus records consist of public properties whose names correspond to the names of the data bus bits, as well as their own methods.

An example data bus class:

To access the value of a bit in a data bus record, you can use its name directly, e.g. string index = record.index; or record.index = "new_name";.

Data bus records are passed to the algorithm as the dynamic type, and the data returned by the algorithm is converted from dynamic to the type of the defined output data bus (the data bus class name is generated automatically by the system).

Project parameters are available in the code as a Hashtable, which means you can access them by name. Keep in mind that their values are of type object , so they must be cast to the appropriate type, for example: int idc = Convert.ToInt32(parameters["customerId"]).

The template generated in the Code field depends on the selections made in the Target result and Code part options.

Below are the templates generated for the following combinations of these options:

  • Target result = Create busbar from the result and Code part = Classes
  • image
    public class UserOperator1_Class
    {
       public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
       {
         return data.Select(record => CreateOutRecord(record)).ToList();
       }
    
       public dynamic CreateOutRecord(dynamic record)
       {
         var outRecord = new 
         { 
    
         };
         return outRecord;
      }
    }

    The template consists of an entire class that defines the operator. The main method that is invoked by the system is:

    List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)

    As parameters, the method accepts a list of busbar records data and a parameter hashtable parameters. It calls the CreateOutRecord method using LINQ. Of course, you can always replace this call with a foreach loop or any other construct, for example:

    public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
       {
    			List<dynamic> result = new List<dynamic>();
    		  foreach (dynamic record in data)
    		  {
    		  	 result.Add(CreateOutRecord(record));
    		  }
    		  return result;
         //return data.Select(record => CreateOutRecord(record)).ToList();
       }
  • Target result = Create busbar from the result and Code part = Main method only
  • image
    public dynamic UserOperationForRecord(dynamic record, Hashtable parameters)
    {
         var outRecord = new 
         { 
    
         };
         return outRecord;
    }

    The template consists of a method that is invoked for each busbar record, with the following signature: dynamic UserOperationForRecord(dynamic record, Hashtable parameters). As parameters, it accepts the currently processed busbar record: record, and the project parameters: parameters.

icon
Remember that when the Target result = Create busbar from the result, you must return the data of a new busbar. This can be an anonymous object.

For example,

   public dynamic UserOperationForRecord(dynamic record, Hashtable parameters)
   {
     var outRecord = new 
     { 
				IndexFullName = record.index + " " + record.index_name
     };
     return outRecord;
  }

Alternatively, in addition to the operator class, you can define your own class and return its instance.

  • Target result = Value into an existing column and Code part = Classes
  • image
    public class UserOperator1_Class
    {
        public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
        {
            return data;
        }
    }

    The template consists of a class definition and a method that is invoked by the system, with the following signature: List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters). The method parameters are a list of objects representing records of the input busbar data and the project parameters parameters. The function must return a list of objects that conforms to the definition of the operator’s input busbar.

  • Target result = Value into an existing column and Code part = Main method only
  • image
    public dynamic UserOperationForRecord(dynamic record, Hashtable parameters)
    {
        return record;
    }

    The generated template contains only a single function with the following signature:

    dynamic UserOperationForRecord(dynamic record, Hashtable parameters)

    The method parameters are an object representing the currently processed busbar record: record and the project parameters: parameters. The function must return an object that conforms to the definition of the input busbar.

icon
Remember that when the Target result = Value into an existing column, you must return objects that conform to the definition of the function’s input objects.

By using the Compile and Test buttons, you can verify the correctness of the entered code and test it on a prepared input busbar record.

image

If an error occurs, the system will return information about the error and the line of code where it occurred.

image

OUTPUT BUSBAR DEFINITION

The Columns tab, which is responsible for defining the output busbar fields, is visible only when you set Target result = Create busbar from the result in the configuration, meaning that you intend to create a completely new output busbar for the operator.

On the Columns tab, the operator must have a defined list of output busbar fields.

image

You can achieve this in two ways:

  1. You start by writing the code, and the system will automatically generate the busbar fields based on the properties of the returned type, for example:
  2. The code returns a list of objects of the ResultUserOperator1 class, which has two properties: stringBit and intBit. By clicking the Autogenerate columns button on the Columns tab, the system will automatically generate the bus bits based on the returned data.

    image
  3. You manually enter the returned bits and their types.
  4. image

    Generating the code template after manually entering the names of the returned bits will result in code being generated with the expected return type.

    public class UserOperator1_Class
    {
       public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
       {
         return data.Select(record => CreateOutRecord(record)).ToList();
       }
    
       public dynamic CreateOutRecord(dynamic record)
       {
         var outRecord = new 
         { 
             manualStringBit = default(String),
             manualIntBit = default(Int32?)
         };
         return outRecord;
      }
icon
After changing the output data, you may need to regenerate the columns on the output bus.
icon
Remember that changing the names of input bus bits or removing bits used in the algorithm will cause the operator to fail if those bits were referenced in the operator’s code.

OPERATOR USAGE EXAMPLE

Let us assume that system A generates a production order report in .xlsx format that looks as follows:

image

In this scenario, GRAVITY is used to import data into another system, B. The data table into which we will import the data has the following structure:

CREATE TABLE e.production_orders
(
	id serial NOT NULL,
	work_order_no character varying,
	article character varying,
	date timestamp without time zone,
	quantity double precision,
	CONSTRAINT production_orders_pkey PRIMARY KEY (id)
)

To achieve this, the data must be transformed from a cross-tab table into a flat list and either filled with zeros or have empty values discarded (depending on the chosen assumptions).

The GRAVITY system does not provide a dedicated operator to achieve this goal. Nevertheless, by using the User operator, you can achieve the intended result.

The process may look as follows:

image

When analyzing the problem, we can see that after loading the file we will have all the data in the form of a list, with columns equal to the total number of production orders and the indexes occurring in them, + 1 (the date column), and with rows equal to the number of days + 2 (the orders row and the items row). In addition, all bits will be of type Text, because it is not possible to specify a type for a column. The data bus will look as follows:

image

The role of the code in the User operator is to process the data from the format described above.

It is therefore clear that the output bus will be different from the input bus.

When configuring the operator, in the first step we define the properties Target result = Create busbar from the result and Code part = Classes. There is no need to include any external libraries or add any additional using directives.

image

The operator code can be as follows:

Because the number of columns is variable and depends on the number of orders and items, working with objects representing bus records is not possible. In the code above, the AsHashtable() method of the class representing the bus is used to switch from working with an object to working with a hash table, which allows values to be accessed by field name.

The bus bits on the Columns tab must be entered manually by specifying their names and data types according to the names of the properties of the objects returned by the function.

image

The result of the configured operator will be an output bus in the following form:

image

At this point, the data format can be imported and is compatible with the table structure in system B.