Przykład projektu z wykorzystaniem operatora USER OPERATOR.
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);
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);
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
. - 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.
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;
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.
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
- Target result = Create busbar from the result oraz Code part = Main method only
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();
}
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
.
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
- Target result = Value into en existing column oraz Code part = Main method only
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.
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.
Używając przycisków Compile oraz Test możesz sprawdzić poprawność wpisanego kodu oraz przetestować na spreparowanym rekordzie magistrali wejściowej.
W przypadku wystąpienia błędu system zwróci informacje o błędzie i linijce kodu gdzie błąd wystąpił.
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.
Możesz to zrealizować na dwa sposoby:
- Zaczynasz od napisania kodu, a system sam wygeneruje bity magistrali na podstawie właściwości zwracanego typu np.
- Wprowadzasz ręcznie zwracane bity i ich typy.
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.
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;
}
PRZYKŁAD UŻYCIA OPERATORA
Załóżmy, że system A generuje raport zleceń produkcyjnych w formacie .xlsx wyglądający następująco:
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:
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:
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
.
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.
Wynikiem działania zbudowanego operatora będzie magistrala wyjściowa w postaci:
W tym momencie format danych jest możliwy do zaimportowana i zgodny z strukturą tabeli w systemie B.