Budowanie instrukcji warunkowych z pliku konfiguracyjnego/tekstowego

Budowanie instrukcji warunkowych z pliku konfiguracyjnego/tekstowego
VarrComodoo
  • Rejestracja:prawie 14 lat
  • Ostatnio:około 18 godzin
  • Lokalizacja:Bk
  • Postów:480
0

Wprowadzając do kontekstu zagadnienia,

Artykuł produkcyjny w ERP ma zdefiniowane m.in listę materiałową (z czego się składa) oraz marszrutę technologiczną (opisującą proces produkcyjny tego artykułu)
Marszruta technologiczna, składa się z operacji technologicznych ułożonych we właściwiej kolejności odwzorowującej proces produkcyjny danego wyrobu, różne wyroby różnie się produkuję dlatego też mają różne marszruty.
Każda operacja technologiczna ma parę parametrów m.in. kod stanowiska na jakim jest wykonywana, czasy normatywne, dział produkcyjny na jakim jest to stanowisko, zasoby itp.

Narzędzie poniżej służy do hurtowej zmiany jednego z parametrów operacji technologicznej (Tn) we wszystkich marszrutach zdefiniowanych w ERP, w zależności od tego jaka operacja technologiczna i z jakimi parametrami jest po danej operacji w tej samej marszrucie.

Generalnie narzędzie miało uporządkować coś raz i dlatego kod do niego wygląda tak jak niżej, czyli wszystko było w kodzie. Teraz obserwuje zjawisko ze coraz częściej są wprowadzane kolejne reguły globalne i coraz częściej musze to edytować w kodzie, chciałbym to zmienić.
Najpierw jak to wygląda teraz.

Kopiuj
foreach (var marszruta in listaMarszrut)
{
    for (int i = 0; i < marszruta.ListaOperacji.Count - 1; i++)
    {
        // (1) czas Tn pomiedzy roznymi działami.
        if (marszruta.ListaOperacji[i].DzialOperacji != marszruta.ListaOperacji[i + 1].DzialOperacji)
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "7.5";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }

        // (2) czas Tn pomiedzy operacjami na tych samych działach
        if ((marszruta.ListaOperacji[i].DzialOperacji == marszruta.ListaOperacji[i + 1].DzialOperacji))
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "0";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }

        // (3) czas Tn dla operacji jezeli najpierw jest magazyn a potem inny dzial niz magazyn
        if (marszruta.ListaOperacji[i].DzialOperacji == DzialEnum.Magazyn && marszruta.ListaOperacji[i + 1].DzialOperacji != DzialEnum.Magazyn)
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "16.01";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }

        // (4) czas Tn dla operacji magazynu jezeli po niej jest kolejna operacja magazynu
        if (marszruta.ListaOperacji[i].DzialOperacji == DzialEnum.Magazyn && marszruta.ListaOperacji[i + 1].DzialOperacji == DzialEnum.Magazyn)
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "0.01";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }

        // (5) czas Tn dla operacji Montaz0101 jezeli następną operacją jest operacja na dziale montazu pierwszego
        if (marszruta.ListaOperacji[i].KodStanowiska == "Montaz0101" && marszruta.ListaOperacji[i + 1].DzialOperacji == DzialEnum.MontazPierwszy)
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "2.25";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }
        // (6) czas Tn dla operacji Spawanie jezeli po niej jest operacja na Slusarni lub Warsztacie
        if (marszruta.ListaOperacji[i].KodStanowiska == "Spawanie" && (marszruta.ListaOperacji[i + 1].DzialOperacji == DzialEnum.Warsztat || marszruta.ListaOperacji[i + 1].DzialOperacji == DzialEnum.Slusarnia))
        {
            marszruta.ListaOperacji[i].CzasNakl_h = "23.01";
            marszruta.ListaOperacji[i].CzyZmieniona = true;
        }
//(..duzo duzo wiecej regul...)
    }

}

W tej pętli jest lista około 30 reguł od najbardziej ogólnej do najbardziej szczegółowych, chciałbym to zoptymalizować i uwolnić się od grzebania w kodzie, chciałbym to zrobić w dwóch etapach:

1 Etap - na już aby możliwie najszybciej zleceniodawca mógł sam już to robić, chciałbym te warunki z ifów pobierać z pliku tekstowego który w odpowiednim formacie (prostym dla nie programisty) mógłby tworzyć pracownik, importować te reguły do programu i program miałby to przerobić na instrukcje warunkowe w ifach.

2 Etap - docelowo ale może trochę poczekać, chciałbym definiowanie tych reguł przenieść do interfejsu użytkownika, tak aby budował sobie reguły wybierając kolejne opcje z ComboBoxów , Check'oów, RadioButton'ów itp.

Coś na kształt budowy kolejnych filtrów w Excelu.
Przechwytywanie.PNG

To co chodzi mi po głowie:

  1. Zbudować szkielety możliwych instrukcji warunkowych z wymiennymi parametrami w formie metod, i na bazie pobieranych danych z pliku konfiguracyjnego wybierać właściwą metodę, przekazywać pobrane parametry do niej i 'odkładać' ją do delegata - a po pobraniu wszystkich uruchomić wszystko z delegata - z tym że tu mam problem bo zależy mi aby uruchamiać te metody we właściwej kolejności od reguł najbardziej ogólnych do najbardziej szczegółowych a tego delegat nie da.

  2. Utworzyć klasę 'Regula' coś na kształt tego niżej, i dane z pliku konfiguracyjnego przekształcać na takie obiekty 'Regul', odkładać je w jakimś słowniku gdzie kluczem byłoby coś (nie wiem jak odwzorować szkielet instrukcji warunkowej na klucz słownika) a wartością - wartość Tn do ustawienia dla danej reguły - ale w tym przypadku o ile reguły jak z kodu wyżej (1-5) jeszcze dałbym rade to dla przypadku (6) gdzie jest jakby zagnieżdżenie nie mam pomysłu jak to zrobić aby było i wygodne i czytelne i skalowalne

Kopiuj
public class Regula {
    public string StanowiskoOperacji { get; set; }            
    public string DzialOperacji { get; set; }
    public string StanowiskoOperacjiNastepnej { get; set; }
    public string DzialOperacjiNastepnej { get; set; }
    public string OperatorPorownania { get; set; }
    public string CzasTn { get; set; }
}

Prośba o nakierowanie jak rozwiązać ten problem, rozwiązanie dla tego przypadku będę na pewno wykorzystywał w innych narzędziach dlatego zależy mi na tym żeby to rozwiązanie było 'takie jak to się powinno robić' ;).


Sterczące kolce Pondijusa, ostre grzebienie Daktyloskopei, Trygla i latający Wieprzoryb są niczym wobec Bestii która nas gnębi...
edytowany 1x, ostatnio: Riddle
ZD
  • Rejestracja:około 3 lata
  • Ostatnio:ponad rok
  • Postów:2310
0

Są takie sytuacje w życiu (inzyniera oprogramowania) że plik konfiguracyjny staje się językiem programowania. Takowym stał się wsad edytora vi czy emacs (tu troche inaczej), pliki konfiguracyjne gier / edytorów / cadów

Wiele z nich ma płynną kompatybilność, w prostych przypadkach plik wygląda jak seria postawień "nazwa.parametru" = "wartość", ale pod maską ma znacznie większe możliwosci, od warunków poczynajac (seria "podstawień" się włacza przy jakiejś opcji)

Lua, Groovy (w ekosystemie JVM), własny język zrealizowany np w Antlr

Tak w ogóle jestem zdziwiony, co to za tzw "ERP" co nie ma języka programowania dla zaawansowanego wdrożeniowca ? Czy to nie "program do faktur" przez marketingowca tak nazwany.


If you put a million monkeys at a million keyboards, one of them will eventually write a Java program - the rest of them will write Perl
VarrComodoo
to stary ERP, z bardzo niewygodnym interfejsem ,to co chce zrobić to trochę "nakładka" jedna z wielu która powstaje dla zoptymalizowania pracy ludzi wprowadzających dane/edytujących dane w tym ERP. Tego się nie przeskoczy - sugerujesz ze olać etap 1, i od razu budować etap 2? wydawało mi się ze to wtórne, idea rozwiązania programistycznego jest najważniejsza a potem czy to z pliku czy z UI to na tym samych mechanizmie by się oparło?
WeiXiao
  • Rejestracja:około 9 lat
  • Ostatnio:około 6 godzin
  • Postów:5105
2

Co do kolejności - mógłbyś do reguły dodać informacje nt. kolejności np. nr. 1, 2, 3, 4, etc.

Cały koncept tego co chcesz nazywa się chyba Rule Engine lub może Query Engine

znalazłem coś takiego https://github.com/microsoft/RulesEngine ale nie analizowałem głębiej, bingnij sobie :D

Kopiuj
[
  {
    "WorkflowName": "Discount",
    "Rules": [
      {
        "RuleName": "GiveDiscount10",
        "SuccessEvent": "10",
        "ErrorMessage": "One or more adjust rules failed.",
        "ErrorType": "Error",
        "RuleExpressionType": "LambdaExpression",
        "Expression": "input1.country == \"india\" AND input1.loyaltyFactor <= 2 AND input1.totalPurchasesToDate >= 5000"
      },
      {
        "RuleName": "GiveDiscount20",
        "SuccessEvent": "20",
        "ErrorMessage": "One or more adjust rules failed.",
        "ErrorType": "Error",
        "RuleExpressionType": "LambdaExpression",
        "Expression": "input1.country == \"india\" AND input1.loyaltyFactor >= 3 AND input1.totalPurchasesToDate >= 10000"
      }
    ]
  }
]

A w takiej bardzo, bardzo prostej wersji to myślałem o czymś takim (bez wsparcia dla innych operatorów niż ==, ale to sobie możesz dopisać)

Kopiuj
public class User
{
    public string Name { get; set; }

    public int Age { get; set; }

    public List<string> JakiesWartosci { get; set; } = new List<string>();
}

public class Rule
{
    public string? Name { get; set; }

    public int? Age { get; set; }

    public string Wartosc { get; set; }
}
Kopiuj
public static void RuleEngine(List<User> users, List<Rule> rules)
{
    foreach (var rule in rules)
    {
        IEnumerable<User> tmp = users;

        if (rule.Name != null)
        {
            tmp = tmp.Where(x => x.Name == rule.Name);
        }

        if (rule.Age != null)
        {
            tmp = tmp.Where(x => x.Age == rule.Age);
        }

        var selectedUsers = tmp.ToList();

        foreach (var user in selectedUsers)
        {
            user.JakiesWartosci.Add(rule.Wartosc);
        }
    }
}
Kopiuj
var users = new List<User>
{
    new User { Name = "Tom", Age = 123 },
    new User { Name = "John", Age = 55 },
    new User { Name = "John", Age = 77 },
};

var rules = new List<Rule>
{
    new Rule
    {
        Name = "John",
        Wartosc = "Wartosc#1"
    },
    new Rule
    {
        Name = "John",
        Age = 55,
        Wartosc = "Wartosc#2"
    },
    new Rule
    {
        Name = "Asdf",
        Wartosc = "Wartosc#3"
    },
    new Rule
    {
        Age = 123,
        Wartosc = "Wartosc#4"
    }
};

RuleEngine(users, rules);

foreach (var user in users)
{
    Console.WriteLine($"Name: {user.Name} Age: {user.Age}");
    Console.WriteLine(string.Join(",", user.JakiesWartosci));
    Console.WriteLine();
}
edytowany 5x, ostatnio: WeiXiao
Wilktar
  • Rejestracja:ponad 5 lat
  • Ostatnio:około 4 godziny
  • Postów:82
1

W jednym z projektów miałem podobny problem. Projekt głównie polegał na wyliczaniu danych z formuł, dodatkowo użytkownik chciał mieć możliwość edytowania tych formuł podczas pracy aplikacji (bez ponownego uruchomienia). Początkowo było to robione za pomocą biblioteki która potrafiła wyliczać takie formuły (trochę jak w excelu), ale niestety co chwilę okazywało się że takie formuły nie są w stanie czegoś wyliczyć i trzeba się nieźle nagimnastykować żeby to zrobić i w dodatku są powolne.

Finalnie skończyło się na tym że formuły były trzymane w postaci kodu c# który był kompilowany przez aplikację a assembly były dynamicznie ładowane, przeszukiwane pod kątem klas które implementują dany interface i wywoływane podczas obliczeń. Gdy użytkownik zmienił formułę to była ona usuwana i ładowana ponownie. Dla użytkowników było to o wiele łatwiejsze niż pisanie formuł jak w excelu, jak ktoś potrafi napisać regułę w excelu to potrafi też napisać if-a :). Dodatkowo byliśmy w stanie dostarczyć użytkownikom niektóre metody/serwisy z aplikacji.

Przyjmując takie rozwiązanie w pliku mogło by być coś takiego:

Kopiuj
class RozneDzialyInstrukcja : Instrukcja
{
    public override void Handle(Operacja operacja, Operacja nastepnaOperacja)
    {
      if(operacja.DzialOperacji != nastepnaOperacja.DzialOperacji)
      {
        operacja.CzasNakl_h = "7.5";
        operacja.CzyZmieniona = true;
      }
    }
}

W naszym przypadku coś takiego sprawdziło się naprawdę nieźle - zwłaszcza że dostęp do edycji formuł miały osoby które były w miarę ogarnięte jeżeli chodzi IT, excela itd. Takie rozwiązanie jest czasochłonne ale daje duże możliwości. Ewentualnie mógłbyś jeszcze popatrzeć na wywoływanie IronPython z c#.


Chciałby nad poziomy człek, a tu ciągle niż,
Nie uciągnie pusty łeb ciężkiej d**y wzwyż.
ZD
  • Rejestracja:około 3 lata
  • Ostatnio:ponad rok
  • Postów:2310
0
Wilktar napisał(a):

Ewentualnie mógłbyś jeszcze popatrzeć na wywoływanie IronPython z c#.

Python jest ogromnie tłustym językiem, i w zasadzie utracił rolę języka wbudowanego
Jak bym zamiast tego proponował Lua


If you put a million monkeys at a million keyboards, one of them will eventually write a Java program - the rest of them will write Perl
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)