Search

Home

USER OPERATOR

USER OPERATOR

icon
Operator User operator pozwala na wprowadzenie dowolnego algorytmu przetwarzającego dane pochodzące z magistrali wejściowej. Tworzenie operatora polega na zadeklarowaniu kodu przetwarzania w języku C#. Podczas kodowania masz dostęp do strumienia danych na magistrali wejściowej oraz parametrów projektu. Możesz sam zdefiniować nowy strumień wyjściowy lub wprowadzić dane na odpowiedni bit magistarli. Kod jest automatycznie kompilowany w trakcie wywołania aplikacji GRAVITY, a utworzone przez Ciebie operatory są obecne w systemie na takich samych prawach jak operatory wbudowane.

Przykład projektu z wykorzystaniem operatora USER OPERATOR.

image

KONFIGURACJA OPERATORA

Do utworzenia operatora wymagana jest choć podstawowa znajomość języka programowania C# ponieważ konfiguracja operatora sprowadza się głównie do zdefiniowana algorytmu przetwarzania danych wejściowych w języku C#.

W pierwszym kroku musisz zdecydować o podstawowych parametrach przetwarzania danych (zakłada User operator properties) przez operator takich jak:

  • Target result → czy operator ma utworzyć zupełnie nową magistralę (Create busbar from the result), czy modyfikować wartości bitów istniejącej magistrali (Value into en existing column);
image

Gdy wybierzesz opcję tworzenia nowej magistrali pojawi się zakładka Columns gdzie musisz wprowadzić definicję bitów magistrali wyjściowej, które będą zgodne z właściwościami obiektów zwracanych z algorytmu.

Wybierając modyfikację istniejących bitów zwróć zmodyfikowane obiekty wejściowe magistrali.

  • Code part czy będziesz potrzebował do zdefiniowania algorytmu całą klasę odpowiedzialną za działanie operatora (Classes) czy wystarczy Ci wywołanie metody dla wywoływanej dla każdego rekordu magistrali (Main method only);
image

Gdy wybierzesz opcję Classes będziesz miał większą kontrolę nad kodem. Możesz tworzyć całą strukturę klasy operatora samodzielnie oraz definiować inne własne klasy.

Wybranie opcji Main method only zaleca się gdy chcesz przeprowadzić proste operacje wyliczeniowe i jesteś w stanie to zrobić w ramach funkcji.

  • Main class name nazwa klasy operatora. System podpowiada nazwę tworzoną automatycznie zapewniając unikalność nazwy w projekcie;
  • Additional use → dodatkowe deklaracje using np: System.Timers.
  • dodatkowe deklaracje dodawaj bez słówka using, po spacji lub enterze.

    Domyślnie są dostępne następujące deklaracje using

    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 czy będziesz potrzebował dodatkowych bibliotek. Możesz użyć przycisku Browse w celu wyszukania biblioteki na dysku lub wprowadzić pełną ścieżkę ręcznie. Pamiętaj również, aby dodać również deklaracje using dla wybranej biblioteki.
image

Użyj przycisku Generate code template w celu wygenerowania szablonu kodu operatora (zakładka Code).

KOD OPERATORA (C#)

Tworząc kod operatora w zależności od ustawienia pola Code part system sam utworzy definicję klasy operatora (po naciśnięciu przycisku Generate code template) i dodaje do niej kod funkcji oraz definicję klasy zdefiniowanej w operatorze.

icon
Możesz dowolnie modyfikować kod wygenerowany przez system (dodawać funkcje, klasy zmienne itp.), ale pamiętaj aby przy ustawieniach: Code part = Classes nazwy klasy w kodzie zgadzała się z nazwą klasy w polu Main class name oraz klasa posiadała funkcję o sygnaturze: List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters) Code part = Main method only istniała funkcja o sygnaturze: dynamic UserOperationForRecord(dynamic record, Hashtable parameters)

System używa typu danych dynamic w celu reprezentacji obiektu będącego rekordem magistrali. Jest to spowodowane różnicą między klasami reprezentującymi poszczególne magistrale i zachowaniem uniwersalności definiowanych algorytmów.

Klasy reprezentujące rekordy magistrali składają się z właściwości publicznych o nazwach zgodnych z nazwami bitów magistrali, oraz własnych funkcji.

Przykładowa klasa magistrali:

public partial class Busbar376Record : IRecord 
    {
        public int? id { get; set; } 
        public string index { get; set; } 
        public string index_name { get; set; } 
        public string code { get; set; } 
        public string unit { get; set; } 
        
        public Busbar376Record() 
        {
        }           
        
        public virtual Hashtable AsHashtable()
        {
            return AsHashtable(this); 
        }        
        
        public static Hashtable AsHashtable(Busbar376Record rec)
        {
            return new Hashtable() {{"id", rec.id},{"index", rec.index},{"index_name", rec.index_name},{"code", rec.code},{"unit", rec.unit}}; 
        }
                
        public System.Collections.Specialized.OrderedDictionary AsOrderedDictionary()
        {
            Busbar376Record rec = this;
            System.Collections.Specialized.OrderedDictionary dict = new System.Collections.Specialized.OrderedDictionary(){{"id", rec.id},{"index", rec.index},{"index_name", rec.index_name},{"code", rec.code},{"unit", rec.unit}};
		        return dict;
        }
        
        
        public static IEnumerable<Hashtable> AsHashtables(IEnumerable<Busbar376Record> records) 
        {
           foreach (Busbar376Record rec in records)
               yield return AsHashtable(rec);
        }               
    }

Aby odwołać się do wartości bitu w rekordzie magistrali możesz użyć jego nazwy

np. string index = record.index; lub record.index = “new_name”;

Dane magistrali są wprowadzane do algorytmu jako typ dynamic a dane zwracane z algorytmu są konwertowane z typu dynamic do typu zdefiniowanej magistrali (nazwa klasy magistrali jest tworzona przez system automatycznie).

Parametry projektowe dostępne są w kodzie jako typ Hashtable, a wiec możesz się do nich dostać po ich nazwie pamiętając, że ich wartość jest typu object więc wymaga rzutowania na odpowiedni typ np. int idc = Convert.ToInt32(parameters[”customerId”]);

Wygenerowany szablon w polu Code jest zależny od wyboru w opcjach Target result i Code part.

Poniżej szablony wygenerowane dla kombinacji tych opcji:

  • Target result = Create busbar from the result oraz 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;
      }
    }

    Szablon składa się z całej klasy definiującej operator. Główna metoda, która zostanie wywołana przez system to:

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

    jako parametry metoda przyjmuje listę rekordów magistrali data oraz tablice parametrów parameters. Wywołuje ona metodę CreateOutRecord używając LINQ. Oczywiście możesz zawsze zmienić to wywołanie na pętlę foreach lub jakąkolwiek inną.

    np.

    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 oraz Code part = Main method only
  • image
    public dynamic UserOperationForRecord(dynamic record, Hashtable parameters)
    {
         var outRecord = new 
         { 
    
         };
         return outRecord;
    }

    Szablon składa się z metody wywoływanej dla każdego rekordu magistrali o sygnaturze dynamic UserOperationForRecord(dynamic record, Hashtable parameters). Jako parametry przyjmuje aktualnie przetwarzany rekord magistrali record oraz parametry projektu parameters.

icon
Pamiętaj, że w przypadku wyboru pola Target result = Create busbar from the result musisz zwrócić dane nowej magistrali. Może to być obiekt anonimowy.

np.

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

lub możesz oprócz klasy operatora zdefiniować własną klasę i zwrócić jej instancję.

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

    Szablon składa się z definicji klasy oraz metody, która zostanie wywołana przez system o sygnaturze: List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters). Parametrami metody jest lista obiektów reprezentujących rekordy magistali wejściowej data oraz parametry projektu parameters. Funkcja musi zwrócić listę obiektów zgodną z definicją magistrali wejściowej operatora.

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

    Wygenerowany szablon posiada tylko jedną funkcję o sygnaturze:

    dynamic UserOperationForRecord(dynamic record, Hashtable parameters)

    Parametrami metody jest obiekt odpowiadający aktualnie przetwarzanemu rekordowi magistrali record oraz parametry projektu parameters. Funkcja musi zwrócić obiekt zgodny z definicją magistrali wejściowej.

icon
Pamiętaj, że w przypadku wyboru pola Target result = Value into en existing column musisz zwrócić obiekty zgodne z definicją obiektów wejściowych do funkcji.

Używając przycisków Compile oraz Test możesz sprawdzić poprawność wpisanego kodu oraz przetestować na spreparowanym rekordzie magistrali wejściowej.

image

W przypadku wystąpienia błędu system zwróci informacje o błędzie i linijce kodu gdzie błąd wystąpił.

image

DEFINICJA MAGISTRALI WYJŚCIOWEJ

Zakładka Columns odpowiadająca za definicje bitów magistrali wyjściowej jest widoczna tylko w przypadku kiedy w konfiguracji ustawiłeś Target result = Create busbar from the result, czyli zamierzasz utworzyć zupełnie nową magistralę wyjściową operatora.

Na zakładce Columns operator musi mieć zdefiniowaną listę bitów magistrali wyjściowej.

image

Możesz to zrealizować na dwa sposoby:

  1. Zaczynasz od napisania kodu, a system sam wygeneruje bity magistrali na podstawie właściwości zwracanego typu np.
  2. public class ResultUserOperator1
    {
    	public string stringBit {get; set;}	
    	public int intBit {get; set;}
    }
    public class UserOperator1_Class
    {
       public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
       {
    	  return data.Select(record => CreateOutRecord(record)).ToList();
       }
    
       public ResultUserOperator1 CreateOutRecord(dynamic record)
       {
         var outRecord = new ResultUserOperator1()
         { 
             stringBit = default(String),
             intBit = default(int)
         };
         return outRecord;
      }
    }

    kod zwraca listę obiektów klasy ResultUserOperator1 posiadającej dwie właściwości stringBit oraz intBit. Klikając na zakładce Columns przycisk Autogenerate columns system sam wygeneruje bity magistrali na podstawie zwracanych danych.

    image
  3. Wprowadzasz ręcznie zwracane bity i ich typy.
  4. image

    Wygenerowanie szablonu kodu po wprowadzeniu ręcznym nazw bitów zwracanych spowoduje wygenerowanie kodu ze spodziewanym typem zwrotu.

    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 
         { 
             manulalStringBit = default(String),
             manulalStringBit = default(Int32?)
         };
         return outRecord;
      }
icon
Po zmianie danych wyjściowych możliwe, że będziesz musiał wygenerować ponownie kolumny na magistrali wyjściowej
icon
Pamiętaj, że zmiana nazw bitów magistrali wejściowej lub usunięcie bitów wykorzystywanych w algorytmie, spowoduje błąd działania operatora, jeśli te bity były użyte w kodzie operatora.

PRZYKŁAD UŻYCIA OPERATORA

Załóżmy, że system A generuje raport zleceń produkcyjnych w formacie .xlsx wyglądający następująco:

image

Zadaniem GRAVITY jest import danych do innego systemu B. Tabela danych, do której będziemy importować dane ma strukturę:

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)
)

W tym celu postać danych musimy zamienić z tabeli krzyżowej na płaską listę i uzupełnić zerami lub odrzucić puste wartości (w zależności od założenia).

System GRAVITY nie posiada dedykowanego operatora pozwalającego osiągnąć ten cel. Mimo to wykorzystując operator User operator możesz uzyskać zamierzony efekt.

Proces może wyglądać następująco:

image

Analizując problem widzimy, że po wczytaniu pliku będziemy posiadali wszystkie dane w formie listy z kolumnami będącymi sumą zleceń produkcyjnych i występujących w nich indeksach + 1 (kolumna data) oraz wierszami w liczbie ilość dni + 2 (wiersz zleceń i artykułów). Dodatkowo wszystkie bity będą typu Text, ponieważ nie da się wyszczególnić typu dla kolumny. Magistrala danych będzie wyglądała następująco:

image

Zadaniem kodu w operatorze User operator będzie przetworzenie danych z powyższego formatu.

Widać zatem, że magistrala wyjściowa będzie różna od magistrali wejściowej.

Konfigurując operator w pierwszym kroku definiujemy właściwości Target result = Create busbar from the result oraz Code part = Classes. Nie musimy załączać żadnych bibliotek zewnętrznych ani dodatkowych dyrektyw using.

image

Kod operatora może być następujący:

public class UserOperator1_Class
{
    public List<dynamic> UserOperation(List<dynamic> data, Hashtable parameters)
    {
	    int rowHeader = 2;
	    int colHeader = 1;

	   	Hashtable htWorkOrders = data[0].AsHashtable();
	    Hashtable htArticles = data[1].AsHashtable();	   	

			List<dynamic> result = new List<dynamic>();			
	    for(int i = rowHeader ; i< data.Count; i++)
	    {	    
	    	Hashtable htData = data[i].AsHashtable();
	    	DateTime operationDate = Convert.ToDateTime(htData["Column0"].ToString());    	

		    for(int j = colHeader; j< htData.Count; j++)
		    {
					if (htData["Column"+j]!=null)
		    	{
			    	string workOrderNo = htWorkOrders["Column"+j].ToString();
		    		string article = htArticles["Column"+j].ToString();
		    		double quantity = Convert.ToDouble(htData["Column"+j].ToString().Replace('.', ',')));
		    		
		    		result.Add(new { Date = operationDate, WorkOrderNo = workOrderNo, Article = article, Quantity = quantity});
	    		}
		    }
	    }
        return result;
    }
}

Ponieważ ilość kolumn jest zmienna i zależy od ilości zleceń i towarów, praca na obiektach reprezentujących rekord magistrali jest niemożliwa. W powyższym kodzie wykorzystana została metoda AsHashtable(), klasy reprezentującej magistralę, w celu przejścia z pracy na obiekcie do tabeli haszującej pozwalającej na odwoływanie się do wartości z wykorzystaniem nazwy pola.

Bity magistrali na zakładce Columns będziemy musieli wprowadzić ręcznie wpisując ich nazwy oraz typy danych zgodnie z nazwami właściwości obiektów zwracanych z funkcji.

image

Wynikiem działania zbudowanego operatora będzie magistrala wyjściowa w postaci:

image

W tym momencie format danych jest możliwy do zaimportowana i zgodny z strukturą tabeli w systemie B.