1. Po co to jest
Przejście hali to największy pojedynczy koszt kompletacji i odkładania. Kolektor układa więc obie listy — odłożenia i kompletacji — w kolejności obchodzenia magazynu.
Pozycje sortuje jedna liczba policzona z adresu lokalizacji: klucz trasy. Magazynier idzie od pierwszego przystanku do ostatniego bez zawracania, a kolejność jest zawsze taka sama, więc da się jej nauczyć.
Alternatywą jest porządek, który o hali nie wie nic — nazwa towaru, numer pozycji na dokumencie, rodzaj podpowiedzianej lokalizacji. Każdy z nich ustawia listę wobec magazynu przypadkowo: A-04-01, A-06-01, a zaraz po nich A-01-01, czyli powrót pod regał, przy którym się już stało.
TrasaDrogaTests; model geometrii jest niezależny od wzoru klucza, więc mierzy drogę, a nie sam siebie):
| porządek listy | droga | |
|---|---|---|
| nazwa towaru — kolejność bez wiedzy o hali | 408–514 m | — |
| naiwny alfabet adresu | 118–137 m | lepiej, ale wraca na początek każdej alejki |
| klucz trasy (serpentyna) | 91–96 m | −79,7% wobec nazwy towaru |
Drugi wiersz jest tu ważniejszy od pierwszego: samo posortowanie po adresie załatwia większość zysku, ale zostawia magazyniera z powrotem na początek każdej alejki. Dopiero serpentyna — opisana w następnej sekcji — zbiera te ostatnie ~25%.
2. Jak liczy się trasa
Wzorzec to serpentyna (S-shape): wchodzisz w alejkę, idziesz do końca i wychodzisz drugą stroną, zamiast wracać po własnych śladach.
Kolejność wewnątrz alejki
Kolumna regału jest obsługiwana w całości — obie strony, od podłogi w górę — a dopiero potem idzie się do kolumny dalej. Klucze poniżej są z hali, w której strefa ma numer 1:
| adres | klucz | co się zmieniło |
|---|---|---|
A-01-00 | 1 000 000 001 000 | start: pierwsza kolumna, podłoga |
B-01-00 | 1 000 000 001 001 | naprzeciwko, druga strona alejki (+1) |
A-01-01 | 1 000 000 001 010 | półka wyżej, ta sama kolumna (+10) |
B-01-01 | 1 000 000 001 011 | znów naprzeciwko |
A-02-00 | 1 000 000 002 000 | dopiero teraz kolejna kolumna (+1000) |
C-09-00 | 1 000 019 990 000 | następna alejka — wejście od dalekiego końca |
C-01-00 | 1 000 019 998 000 | w tej alejce kolumna 01 jest ostatnia |
2 000 000 001 000, a w strefie bez numeru — 999 000 000 001 000, czyli koniec trasy. Skąd numer, mówi następna sekcja.3. Parowanie rzędów — pierwsza rzecz do ustawienia
Cecha danych na magazynie. Mówi, ile rzędów magazynier obsługuje w jednym przejściu alejką.
- gdzie
- kartoteka magazynu → cecha
Parowanie rzędów(liczba całkowita) - kto zakłada
- akcja menu Magazyn → Kolektory → Czynności → WebAPI → Konfiguracja
- zakres
- per magazyn — dwie hale tej samej bazy mogą mieć różny układ
| wartość | znaczenie |
|---|---|
| pusta / 0 | 2 — wartość domyślna |
| 2 | regały stoją parami naprzeciw siebie: (A,B) to jedna alejka, (C,D) następna |
| 1 | każdy rząd ma własny korytarz — osobne przejście |
| 3 i więcej | tyle rzędów na alejkę; układ nietypowy, ale wzór to przyjmie |
| hala | klucz 2 | klucz 1 |
|---|---|---|
| regały parami | 24,0 m | 24,0 m |
| osobne korytarze | 350,4 m | 187,2 m |
1 jest obowiązkiem wdrożeniowym, a nie strojeniem.
4. Kolejność strefy — druga rzecz do ustawienia
Cecha danych na elemencie słownika. Mówi, którą strefę magazynier obchodzi pierwszą — i którą zapełnia pierwszą.
- gdzie
- element słownika Strefa → cecha
Kolejność strefy(liczba całkowita) - kto zakłada
- akcja menu Magazyn → Kolektory → Czynności → WebAPI → Konfiguracja
- zakres
- per strefa — numerujesz od 1, w kolejności obchodzenia
| wartość | trasa | odkładanie |
|---|---|---|
| 1, 2, 3… | kolejność obchodzenia — 1 pierwsza | zapełniasz w tej samej kolejności |
pusta / 0 | strefa idzie na koniec trasy | nie jest proponowana do odkładania |
Pusta wartość znaczy dwie rzeczy naraz, bo obie wynikają z jednego faktu: „ta strefa nie jest miejscem składowania”. Na trasę wchodzi mimo to — może tam coś leżeć i trzeba po to dojść — ale sugestia odłożenia jej nie zaproponuje. Typowe ustawienie: hala 1, namiot na gabaryty 2, a przyjęcie / kontrola / wydanie bez wartości.
M (Magazynowania) i nadaje jej numer 1 — na tym jej rola się kończy. Każdą kolejną strefę zakładasz Ty i Ty wpisujesz jej numer, jeśli klient tego potrzebuje. Pamiętaj tylko, co znaczy brak numeru: strefa nie rozdziela trasy i nie jest proponowana do odkładania. Drugiemu miejscu składowania (namiot, antresola) nadaj więc numer zanim ruszy pierwsze odkładanie.Numer strefy jest stwierdzeniem o hali, a stała — intencją wobec jednego towaru; przy zamykaniu strefy wygrywa hala, bo intencja powstała, gdy hala wyglądała inaczej. Stała ustępuje tu tak samo, jak ustępuje od zawsze, gdy towar się w niej nie mieści.
W bazie, w której nikt nie numerował stref, nic się nie zmienia — reguła włącza się dopiero wtedy, gdy numeracja jest faktycznie używana. Nie dotyczy też pobierania: towar leżący w strefie bez numeru nadal trzeba stamtąd zabrać, więc kompletacja go widzi (idzie tylko na koniec trasy).
5. Cecha TrasaMagazynu
Cecha algorytmiczna — sięgasz po nią dopiero wtedy, gdy parowanie rzędów i kolejność stref nie wystarczają.
- gdzie
- Narzędzia → Opcje → Systemowe → Definicje cech, tabela
CfgNodes, zakładka Algorytm - kto zakłada
- akcja menu Magazyn → Kolektory → Czynności → WebAPI → Cecha trasy magazynu
- typ
- Liczba rzeczywista (patrz pułapki)
- zakres
- globalny — ten sam kod dla każdego magazynu, rozdziel je po
magazyn.Symbol
Po założeniu nic się nie zmienia: wstawiony algorytm woła wzór dodatku, więc trasa zostaje ta sama. Zmienia się tylko to, kto ją liczy — od tej chwili kod z bazy, nie z DLL.
public double Get_Feature_TrasaMagazynu(
Soneta.Magazyny.Magazyn magazyn,
Soneta.Handel.DokumentHandlowy lokalizacja,
string operacja,
string strefa, string rzad, int pion, int poziom)
Mniejsza liczba = wcześniej na trasie. Każda zwrócona wartość jest odpowiedzią — także 0 (to legalny klucz pierwszego przystanku, a nie „policz to za mnie”).
Skompilowany algorytm siedzi w pamięci procesu. Sesje WebAPI są per żądanie, więc wszystko niżej odświeża się samo — ale nie ten cache.
Rekompilacja unieważnia algorytm we wszystkich procesach naraz i mija też cache dyskowy. Kosztuje jednorazowo około sekundy — i na tę chwilę czekają wszystkie równoległe żądania, więc nie rób tego w szczycie kompletacji.
Założenie i skasowanie cechy działa natychmiast, bez rekompilacji — dotyczy ona wyłącznie zmiany ciała.
6. Gotowe warianty
Wszystkie siedzą w komentarzu wstawianym do cechy przy zakładaniu. Poniżej te, po które sięga się najczęściej.
6.1 Alejka ślepa — zawsze w jedną stronę
Gdy alejka ma jedno wejście i wraca się nią samą, serpentyna nie ma sensu:
int nrRzedu = string.IsNullOrEmpty(rzad) ? 1 : char.ToUpper(rzad[0]) - 'A' + 1;
return nrRzedu * 10000000.0 + pion * 1000.0 + poziom * 10.0;
Parowanie rzędów i Kolejność strefy przestają wpływać na trasę. Powyższy kod nie patrzy na argument strefa, więc cała hala liczy się jak jedna: lokalizacje z namiotu przeplotą się z halowymi. Przy więcej niż jednej strefie składowania rozdziel je w kodzie — członem starszym niż nrRzedu; numer wpisany w cesze sam tu nie dojdzie.Odkładania to nie dotyczy. Numer w cesze
Kolejność strefy dalej rozstrzyga, które strefy sugestia w ogóle proponuje i którą zapełnia najpierw — ta ścieżka Twojego algorytmu nie woła.
6.2 Hala nieregularna — mapa rzędów
Gdy „alejka” przestaje być funkcją litery rzędu: A+B stoją parami, D w poprzek pod ścianą, C na drugim końcu hali. Wypisujesz układ wprost:
int alejka, strona, kierunek;
switch (string.IsNullOrEmpty(rzad) ? ' ' : char.ToUpper(rzad[0]))
{
case 'A': alejka = 0; strona = 0; kierunek = 1; break; // alejka A+B
case 'B': alejka = 0; strona = 1; kierunek = 1; break; // TA SAMA alejka, druga strona
case 'D': alejka = 1; strona = 0; kierunek = 1; break; // regał w poprzek, pod ścianą
case 'C': alejka = 2; strona = 0; kierunek = -1; break; // drugi koniec hali — wracamy nim
default: alejka = 8; strona = 0; kierunek = 1; break; // nowy rząd: na KONIEC trasy
}
double pionTrasa = kierunek > 0 ? pion : 9999 - pion;
return alejka * 10000000.0 + pionTrasa * 1000.0 + poziom * 10.0 + strona;
Trzy rzeczy robią tu całą robotę: alejka ustala kolejność obchodzenia (D przed C, choć alfabet twierdzi inaczej), strona trzyma A i B w jednym przejściu (ten sam alejka, różna strona ⇒ przeplatają się co pion), a kierunek = -1 to ręczna serpentyna dla regału, którym wracasz ku rampie.
default na końcu (8), nie na początku. Gdy ktoś doda kiedyś rząd E i zapomni o cesze, ma wylądować za wszystkim, a nie rozerwać trasę w środku.Ten wariant też liczy klucz sam, więc
Parowanie rzędów i Kolejność strefy przestają wpływać na trasę — układ opisujesz tutaj w całości, ze strefami włącznie. Na odkładanie numer strefy działa dalej: to osobna ścieżka, która Twojego algorytmu nie woła.
6.3 Nieregularność wewnątrz rzędu — numer na lokalizacji
Gdy nawet mapa rzędów nie wystarcza (przerwa w numeracji pionów, regał dostawiony bokiem), zostaje sposób dużych WMS-ów: pick sequence wpisany ręcznie na lokalizacji. Załóż cechę liczbową PickSeq na tabeli DokHandlowe (dokument LOK to lokalizacja):
if (lokalizacja != null)
{
var seq = lokalizacja.Features["PickSeq"];
if (seq != null && System.Convert.ToDouble(seq) > 0)
return System.Convert.ToDouble(seq);
}
return EndIF.Magazyn.Lokalizacje.TrasaMagazynu.Wzor(magazyn, lokalizacja, strefa, rzad, pion, poziom);
PickSeq spadają do wzoru, więc mapę można budować stopniowo.7. Operacje — kto pyta o trasę
Argument operacja pozwala dać każdemu procesowi własny kierunek.
| wartość | skąd przychodzi |
|---|---|
"Odlozenie" | lista odłożenia po przyjęciu (PZ, MMP) |
"Wydanie" | kompletacja wydania — lista kompletacyjna, WZ |
"PrzesuniecieMM" | kompletacja przesunięcia międzymagazynowego (MMW) |
"Lokalizacje" | pasek lokalizacji z zasobem na karcie towaru |
Wydanie i przesunięcie są rozdzielone, mimo że kompletują tym samym kodem. To podział procesowy, nie techniczny: towar z WZ jedzie na rampę wydań, a z MMW na rampę przesunięć — u części klientów są to dwa różne końce hali.
double klucz = EndIF.Magazyn.Lokalizacje.TrasaMagazynu.Wzor(magazyn, lokalizacja, strefa, rzad, pion, poziom);
if (operacja == "Wydanie") return -klucz; // rampa wydań na drugim końcu hali
return klucz;
TrasaMagazynu.Wzor — to jedyne wywołanie, które czyta ustawienia hali (Parowanie rzędów, Kolejność strefy). Surowe KolejnoscTrasy.Oblicz(strefa, rzad, pion, poziom) policzy klucz, ale oba te ustawienia po cichu unieważni.Trasa a sugestia odłożenia
Przy odkładaniu pracują dwa mechanizmy, jeden po drugim. Łatwo je pomylić, bo oba mówią o lokalizacjach:
| co ustala | czym |
|---|---|
| Dokąd pojedzie towar — jeden adres na pozycję | Sugestia odłożenia |
| W jakiej kolejności magazynier obejdzie wskazane adresy | ta strona — klucz trasy z operacją "Odlozenie" |
Kolejność jest sztywna: najpierw wybór, potem trasa. Klucz liczy się dopiero dla lokalizacji, którą sugestia już wskazała — Twój algorytm dostaje ją jako fakt i nie ma jak jej zmienić.
TrasaMagazynu na wybór lokalizacji nie wpływa w ogóle.8. Pułapki
Granica dokładności własnego wzoru
Cecha algorytmiczna nie ma w Sonecie typu długiego (FeatureTypeNumber zna Int i Double, nie Long), a klucz mnoży numer strefy przez 1012. Int kończy się na 2 147 483 647, więc nie mieści nawet samego członu strefy — stąd Liczba rzeczywista. Ale i ona liczy całkowite co do jedności tylko do 253 (9,007 · 1015).
TrasaMagazynu.Wzor, którego wynik mieści się zawsze.Liczba argumentów musi zgadzać się co do jednego
FeatureDefinition.CheckArgs porównuje ją z sygnaturą metody, więc zostaw sygnaturę dokładnie taką, jaką wstawiła akcja. Skreślenie argumentu, którego nie używasz (np. lokalizacja albo operacja), tak samo jak dopisanie własnego, sprawi że cecha przestanie działać — nie błędem, tylko cichą degradacją do wzoru wbudowanego, ze śladem w dzienniku. Nieużywane argumenty po prostu zignoruj.
null jest bezpieczny: CheckArgs przepuszcza go dla typów referencyjnych. Lokalizacja bez strefy (strefa == null) ani wywołanie bez dokumentu niczego nie wywracają — ale sprawdzaj lokalizacja != null, zanim sięgniesz po jego cechy.
9. Diagnostyka
Co dodatek mówi sam
Każde odrzucenie algorytmu zostawia ślad w dzienniku modułu, z przedrostkiem [TrasaMagazynu]:
| komunikat | znaczy |
|---|---|
| „cecha … nie dała wyniku (wyjątek) — użyto wzoru wbudowanego” | algorytm się wywrócił albo ma złą liczbę argumentów |
| „klucz trasy … przekracza 2^53 …” | własny wzór zwraca za duży klucz; leci raz na proces, nie przy każdej lokalizacji |
