W ostatnich dwóch artykułach omówiono ogólnie pięć kroków do zaprojektowania wbudowanej architektury oprogramowania. Do tej pory skupiono się na potrzebie rozdzielenia jej na zależną i niezależną od sprzętu. Wskazano również korzyści płynące z identyfikowania zestawów danych systemu na wczesnym etapie procesu. To, co ujawniły analizy, to istotna zasada — dane dyktują strukturę projektu.
Pięć etapów projektowania architektury oprogramowania wbudowanego, które w tej serii poddawane są analizie obejmuje:
1. Oddziel ją;
2. Zidentyfikuj i śledź zestawy danych;
3. Rozłóż system na czynniki pierwsze;
4. Zaprojektuj interfejs i komponenty;
5. Symuluj, iteruj i skaluj.
W poniższym materiale przybliżono trzeci krok: Dekompozycję systemu.
Zanim jednak zagłębimy się w wątek, pomocne byłoby posiadanie zarysu całości i ogólnych diagramów pozwalających na lepsze zrozumienie działania projektowanego sprzętu. Załóżmy na przykład, że mamy system wbudowany, urządzenie, które odczytuje informacje dotyczące temperatury i polecenia przez interfejs szeregowy. Na podstawie komend i danych o termice sprzęt sygnalizuje użytkownikowi stan w jakiś sposób za pomocą wyświetlacza i diody LED. Urządzenie będzie również sterować przekaźnikiem i silnikiem. Całość może wyglądać podobnie do rysunku 1.
Pierwszy krok nakazywałby rozpoczęcie projektowania architektury oprogramowania poprzez oddzielenie struktury niezależnej i zależnej od sprzętu. Na przykład można zacząć od utworzenia diagramu podobnego do rysunku 1 w pierwszej części tego cyklu.
Drugim etapem jest identyfikacja i śledzenie pakietów danych w systemach. W ramach pierwszego kroku można zestawić te ostatnie, jednak wizualizacja architektury i zasobów jest znacznie bardziej produktywna i pomaga dyktować bardziej intuicyjną platformę. Na przykład identyfikacja danych może dać diagram podobny do rysunku 2.
Rys.2. Zestawy danych są weryfikowane na podstawie części architektury oprogramowania, do której należą.
Z powyższego diagramu wynikają dwie różne architektury i widać, gdzie w projekcie będą znajdować się odmienne pakiety danych. W tym momencie niekoniecznie istotne jest, czy nie próbowaliśmy zbytnio uporządkować diagramu. Można zmienić układ, a następnie dodawać strzałki, aby pokazać, jak dane mogą przepływać przez system. Zwykle, gdy to jest zrobione, złożoność diagramu dramatycznie wzrasta. Szybką próbę widać poniżej, na rysunku 3.
Rys.3. Przykładowy diagram przepływu danych pokazuje, w jaki sposób te przemieszczają się i wchodzą w interakcje w całym systemie.
Widać, gdzie niektóre zestawy danych nie wchodzą w interakcje. Zasoby osierocone sugerują, że w naszej początkowej architekturze może brakować interakcji lub innych pakietów danych. Pamiętać jednak trzeba, że architektury oprogramowania ewoluują. W tym momencie nie stresujemy się ani nie martwimy o to. W rzeczywistym projekcie z czasem uzupełnimy brakujące luki. Dążenie do perfekcji spowolni organizację rzeczy na dłuższą metę. Trzeba zauważyć również, że wiele zasobów wpływa do bloku sprzętowych wyjść danych w części architektury zależnej od sprzętu. Tak duża ilość zasobów przepływających do jednego miejsca sugeruje, że blok danych wyjściowych jest zbyt uproszczony i będzie wymagał dalszego rozproszenia w przyszłej iteracji.
Krok #3 — rozłóż system na podstawowe elementy
Gdy zespół uzyska wstępną ocenę zestawów danych i sposobu ich poruszania się w systemie, jesteśmy gotowi do rozdrobnienia architektury oprogramowania do czynników pierwszych. Dekompozycja nie pozwala naszemu oprogramowaniu popaść w chaos! Zamiast tego jest aktem pobierania zestawów danych i tworzenia bloków konstrukcyjnych, takich jak zadania, wątki i inne mechanizmy organizacyjne.
Sposób rozkładu architekta lub zespołu często zależy od jego celów końcowych i złożoności systemu. Zazwyczaj do opracowania architektury oprogramowania używamy jednej z trzech metod dekompozycji. Techniki te obejmują:
* Rozkład przez bezpieczeństwo;
* Rozkład według domeny;
* Rozkład według zadania.
Przyjrzyjmy się, jak każda z nich może pomóc architektowi oprogramowania wbudowanego.
Rozkład przez bezpieczeństwo
W przypadku systemów z zestawami danych, które muszą być chronione z punktu widzenia bezpieczeństwa, jednym z pierwszych kroków dekompozycji oprogramowania jest dalsze rozbicie architektury na bezpieczne i niezabezpieczone segmenty. Na przykład, gdyby zdekomponować badane przez nas urządzenie sterujące silnikiem, schemat architektoniczny mógłby wyglądać jak na rysunku 4. Dzieje się tak, ponieważ rysunek oddzielił zestawy danych na podstawie domeny bezpieczeństwa, w której powinny się znajdować. (Model zagrożenia i Analiza bezpieczeństwa pozwoliłyby zidentyfikować te zasoby).
Rys.4. Przykład bezpiecznego/niezabezpieczonego partycjonowania danych wbudowanego systemu sterowania silnikiem.
Oddzielenie architektury przez zabezpieczenia może pomóc zidentyfikować zestawy danych i składniki aplikacji, które wymagają specjalnego traktowania. Na przykład te pakiety danych prawdopodobnie będą musiały zostać umieszczone w izolowanym sprzętowo środowisku wykonawczym. Ponadto mogą wymagać skonfigurowania dodatkowych jednostek ochrony pamięci w celu ograniczenia dostępu. Wreszcie mogą istnieć nawet usługi typu Root-of-Trust, które należy wdrożyć, aby zarządzać zestawami danych i uzyskiwać do nich akces.
Rozkład według domeny
Dekompozycja według domeny stała się bardzo popularną metodą projektowania. W najszerszym znaczeniu krok 1, rozdzielenie architektury oprogramowania, już zdekomponował całość na dwie opcje: domenę sprzętu czasu rzeczywistego i aplikacji. Dekompozycja według zabezpieczeń zidentyfikowała również dwie dodatkowe domeny: bezpieczne i niezabezpieczone. Każdą z nich można samodzielnie rozłożyć i zaprojektować niezależnie.
Teoretycznie całość można dalej rozczłonkować na dodatkowe domeny, które pozwalają ekspertowi skoncentrować się na architekturze dla danego ujęcia. Na przykład, rysunek 5 pokazuje, że po niewielkiej zmianie kolejności na rysunku 4 pojawiają się trzy dodatkowe domeny, które prezentują się w danych:
* Domena wejściowa;
* Domena kontroli silnika;
* Domena wyjściowa.
Warto zauważyć, że patrząc w ten sposób, często przecinają one warstwy aplikacji i obejmują wszystko, co ma związek z domeną. Na przykład domena sterowania silnikiem zarządza stanami wysokiego poziomu i współdziała ze sprzętem niskiego, za pośrednictwem warstw abstrakcji. Może nawet zaistnieć potrzeba interfejsu aplikacji do interakcji między domenami, co można zauważyć między tymi wejściowymi i sterującymi silnikami. Nie byłoby niczym niezwykłym użycie RTOS-a do tworzenia kolejek komunikatów dla różnych domen, aby mogły ze sobą współdziałać.
Rozkład według zadania
Ponad połowa systemów wbudowanych korzysta z systemu operacyjnego czasu rzeczywistego (RTOS). Nawet w takich, które nie stosują RTOS-a, dość często używa się synchronizacji czasowej wspólnie do wykonywanych zadań. To ostatnie to logiczne pogrupowanie kodu źródłowego i pomysłów architektonicznych, które są ze sobą powiązane i rozwiązują problem z oprogramowaniem. Tudzież, w tym przypadku, zadanie może hermetyzować zestawy danych oraz operacje i procesy na tych zasobach w wielu domenach oprogramowania.
Korzystne może być rozłożenie początkowej architektury na podstawie zadań. Na przykład, jeśli ponownie spojrzymy na nasze urządzenie sterujące silnikiem, możemy użyć naszej dekompozycji domeny, aby utworzyć trzy operacje:
* Zadanie wejściowe;
* Zadanie kontroli motorycznej;
* Zadanie wyjściowe.
W zależności od systemu może to być w porządku. Jeśli jednak przyjrzymy się szczegółom poszczególnej domeny, można odnieść wrażenie, że każde zadanie będzie próbowało zrobić zbyt wiele. A system może mieć problemy ze skalowaniem w miarę rozwoju. Dlatego zamiast tego, na podstawie zidentyfikowanych przez nas zestawów danych, można skłonić się do podziału zadań, takiego jak pokazano na rysunku 6.
Jak widać, w tym wypadku jest pięć operacji:
1. Zadanie kontrolera;
2. Zadanie czujnika;
3. Zadanie odbiorcze;
4. Zadanie motoryczne;
5. Zadanie wyświetlania.
Ten podział będzie znacznie bardziej elastyczny, ponieważ każda operacja ma przydzieloną jedną odpowiedzialność, na której się koncentruje. Na przykład, jeśli klient w najbliższej przyszłości wymaga diagnostyki, możemy dodać takowe zadanie. Jeśli zostanie uwzględnionych więcej czujników, zadanie czujnika może nimi zarządzać. Jeśli potrzebujemy przesłać stan urządzenia za pomocą systemu telemetrii, możemy dodać operację telemetrii i zadanie transmisji danych. Jeśli zaprojektujemy nowy sprzęt, który wykorzystuje taki sam silnik i sterownik, możemy wziąć nasze zadanie silnikowe sterujące nim i wykorzystać je ponownie!
Wypełnianie szczegółów
Po rozczłonkowaniu naszego systemu na bloki i operacje wysokiego poziomu możemy rozłożyć kilka początkowych architektur komunikacyjnych. Na przykład w ostatniej sekcji widzieliśmy, że nasza bazowa architektura będzie miała pięć zadań. Istnieje kilka obszarów, w których widzimy dane, a ich przepływ przekracza granice między operacjami:
* Zadanie czujnika do zadania kontrolera;
* Zadanie odbiorcze do zadania kontrolera;
* Zadanie sterownika do zadania silnikowego;
* Zadanie kontrolera do wyświetlenia zadania.
Przekroczenie granic sugeruje, że operacje te muszą wchodzić w interakcje, aby wymieniać dane lub komunikować się ze sobą w jakiś sposób. W ramach etapu dekompozycji możemy wyróżnić te powiązania i zidentyfikować potencjalne mechanizmy przesyłania zasobów. Jednak często najlepszą praktyką jest odkładanie decyzji architektonicznych tak długo, jak to możliwe. Jeśli więc nie musimy podejmować jej dzisiaj, powinniśmy ją delegować, aż będziemy zmuszeni ją zawiązać. Na tym etapie możemy na przykład przyjrzeć się interakcji zadania czujnika z operacją kontrolera oraz zdać sobie sprawę, że istnieje kilka rozwiązań:
* Użycie kolejki wiadomości, aby przesłać dane o temperaturze do sterownika;
* Użycie współdzielonej lokalizacji w pamięci (z muteksem, aby chronić zasoby).
Jaką opcję powinniśmy zastosować? W tym momencie jeszcze nie musimy przedsięwziąć tej decyzji! W dalszej części projektu mogą pojawić się inne szczegóły, które zasugerują, która z nich jest najlepszym wyborem. Może oba wyjścia są równie poprawne. Dlatego też, zamiast decydować dzisiaj, należy przenieść to ustanowienie w przyszłość.
Wnioski
Jak zaprezentowano w tym artykule, dane, które zostały zidentyfikowane w poprzednich krokach zaczęły naturalnie dyktować sposób, w jaki system rozkładany jest na domeny i zadania. To, jak zasoby chcą przepływać przez system wskazuje, gdzie operacje będą ze sobą współdziałać. Chociaż zwykle istnieje wiele rozwiązań do obsługi interakcji zadań, często ostateczną decyzję odkłada się na późniejszy etap procesu projektowania. Odraczanie jej zazwyczaj pozwala łatwiej zmieniać i rozwijać projekt, a także ugruntowywać rzeczy tylko wtedy, gdy jest to konieczne.
Widzimy powoli, jak z mgły wyłania się obraz architektury oprogramowania. W kolejnym artykule omówiony zostanie czwarty etap, którym jest identyfikacja interfejsów i komponentów systemu.
Źródło: https://www.embedded.com/5-steps-to-designing-an-embedded-software-architecture-step-3/
Pięć etapów projektowania architektury oprogramowania wbudowanego, które w tej serii poddawane są analizie obejmuje:
1. Oddziel ją;
2. Zidentyfikuj i śledź zestawy danych;
3. Rozłóż system na czynniki pierwsze;
4. Zaprojektuj interfejs i komponenty;
5. Symuluj, iteruj i skaluj.
W poniższym materiale przybliżono trzeci krok: Dekompozycję systemu.
Zanim jednak zagłębimy się w wątek, pomocne byłoby posiadanie zarysu całości i ogólnych diagramów pozwalających na lepsze zrozumienie działania projektowanego sprzętu. Załóżmy na przykład, że mamy system wbudowany, urządzenie, które odczytuje informacje dotyczące temperatury i polecenia przez interfejs szeregowy. Na podstawie komend i danych o termice sprzęt sygnalizuje użytkownikowi stan w jakiś sposób za pomocą wyświetlacza i diody LED. Urządzenie będzie również sterować przekaźnikiem i silnikiem. Całość może wyglądać podobnie do rysunku 1.
Pierwszy krok nakazywałby rozpoczęcie projektowania architektury oprogramowania poprzez oddzielenie struktury niezależnej i zależnej od sprzętu. Na przykład można zacząć od utworzenia diagramu podobnego do rysunku 1 w pierwszej części tego cyklu.
Drugim etapem jest identyfikacja i śledzenie pakietów danych w systemach. W ramach pierwszego kroku można zestawić te ostatnie, jednak wizualizacja architektury i zasobów jest znacznie bardziej produktywna i pomaga dyktować bardziej intuicyjną platformę. Na przykład identyfikacja danych może dać diagram podobny do rysunku 2.
Rys.2. Zestawy danych są weryfikowane na podstawie części architektury oprogramowania, do której należą.
Z powyższego diagramu wynikają dwie różne architektury i widać, gdzie w projekcie będą znajdować się odmienne pakiety danych. W tym momencie niekoniecznie istotne jest, czy nie próbowaliśmy zbytnio uporządkować diagramu. Można zmienić układ, a następnie dodawać strzałki, aby pokazać, jak dane mogą przepływać przez system. Zwykle, gdy to jest zrobione, złożoność diagramu dramatycznie wzrasta. Szybką próbę widać poniżej, na rysunku 3.
Rys.3. Przykładowy diagram przepływu danych pokazuje, w jaki sposób te przemieszczają się i wchodzą w interakcje w całym systemie.
Widać, gdzie niektóre zestawy danych nie wchodzą w interakcje. Zasoby osierocone sugerują, że w naszej początkowej architekturze może brakować interakcji lub innych pakietów danych. Pamiętać jednak trzeba, że architektury oprogramowania ewoluują. W tym momencie nie stresujemy się ani nie martwimy o to. W rzeczywistym projekcie z czasem uzupełnimy brakujące luki. Dążenie do perfekcji spowolni organizację rzeczy na dłuższą metę. Trzeba zauważyć również, że wiele zasobów wpływa do bloku sprzętowych wyjść danych w części architektury zależnej od sprzętu. Tak duża ilość zasobów przepływających do jednego miejsca sugeruje, że blok danych wyjściowych jest zbyt uproszczony i będzie wymagał dalszego rozproszenia w przyszłej iteracji.
Krok #3 — rozłóż system na podstawowe elementy
Gdy zespół uzyska wstępną ocenę zestawów danych i sposobu ich poruszania się w systemie, jesteśmy gotowi do rozdrobnienia architektury oprogramowania do czynników pierwszych. Dekompozycja nie pozwala naszemu oprogramowaniu popaść w chaos! Zamiast tego jest aktem pobierania zestawów danych i tworzenia bloków konstrukcyjnych, takich jak zadania, wątki i inne mechanizmy organizacyjne.
Sposób rozkładu architekta lub zespołu często zależy od jego celów końcowych i złożoności systemu. Zazwyczaj do opracowania architektury oprogramowania używamy jednej z trzech metod dekompozycji. Techniki te obejmują:
* Rozkład przez bezpieczeństwo;
* Rozkład według domeny;
* Rozkład według zadania.
Przyjrzyjmy się, jak każda z nich może pomóc architektowi oprogramowania wbudowanego.
Rozkład przez bezpieczeństwo
W przypadku systemów z zestawami danych, które muszą być chronione z punktu widzenia bezpieczeństwa, jednym z pierwszych kroków dekompozycji oprogramowania jest dalsze rozbicie architektury na bezpieczne i niezabezpieczone segmenty. Na przykład, gdyby zdekomponować badane przez nas urządzenie sterujące silnikiem, schemat architektoniczny mógłby wyglądać jak na rysunku 4. Dzieje się tak, ponieważ rysunek oddzielił zestawy danych na podstawie domeny bezpieczeństwa, w której powinny się znajdować. (Model zagrożenia i Analiza bezpieczeństwa pozwoliłyby zidentyfikować te zasoby).
Rys.4. Przykład bezpiecznego/niezabezpieczonego partycjonowania danych wbudowanego systemu sterowania silnikiem.
Oddzielenie architektury przez zabezpieczenia może pomóc zidentyfikować zestawy danych i składniki aplikacji, które wymagają specjalnego traktowania. Na przykład te pakiety danych prawdopodobnie będą musiały zostać umieszczone w izolowanym sprzętowo środowisku wykonawczym. Ponadto mogą wymagać skonfigurowania dodatkowych jednostek ochrony pamięci w celu ograniczenia dostępu. Wreszcie mogą istnieć nawet usługi typu Root-of-Trust, które należy wdrożyć, aby zarządzać zestawami danych i uzyskiwać do nich akces.
Rozkład według domeny
Dekompozycja według domeny stała się bardzo popularną metodą projektowania. W najszerszym znaczeniu krok 1, rozdzielenie architektury oprogramowania, już zdekomponował całość na dwie opcje: domenę sprzętu czasu rzeczywistego i aplikacji. Dekompozycja według zabezpieczeń zidentyfikowała również dwie dodatkowe domeny: bezpieczne i niezabezpieczone. Każdą z nich można samodzielnie rozłożyć i zaprojektować niezależnie.
Teoretycznie całość można dalej rozczłonkować na dodatkowe domeny, które pozwalają ekspertowi skoncentrować się na architekturze dla danego ujęcia. Na przykład, rysunek 5 pokazuje, że po niewielkiej zmianie kolejności na rysunku 4 pojawiają się trzy dodatkowe domeny, które prezentują się w danych:
* Domena wejściowa;
* Domena kontroli silnika;
* Domena wyjściowa.
Warto zauważyć, że patrząc w ten sposób, często przecinają one warstwy aplikacji i obejmują wszystko, co ma związek z domeną. Na przykład domena sterowania silnikiem zarządza stanami wysokiego poziomu i współdziała ze sprzętem niskiego, za pośrednictwem warstw abstrakcji. Może nawet zaistnieć potrzeba interfejsu aplikacji do interakcji między domenami, co można zauważyć między tymi wejściowymi i sterującymi silnikami. Nie byłoby niczym niezwykłym użycie RTOS-a do tworzenia kolejek komunikatów dla różnych domen, aby mogły ze sobą współdziałać.
Rozkład według zadania
Ponad połowa systemów wbudowanych korzysta z systemu operacyjnego czasu rzeczywistego (RTOS). Nawet w takich, które nie stosują RTOS-a, dość często używa się synchronizacji czasowej wspólnie do wykonywanych zadań. To ostatnie to logiczne pogrupowanie kodu źródłowego i pomysłów architektonicznych, które są ze sobą powiązane i rozwiązują problem z oprogramowaniem. Tudzież, w tym przypadku, zadanie może hermetyzować zestawy danych oraz operacje i procesy na tych zasobach w wielu domenach oprogramowania.
Korzystne może być rozłożenie początkowej architektury na podstawie zadań. Na przykład, jeśli ponownie spojrzymy na nasze urządzenie sterujące silnikiem, możemy użyć naszej dekompozycji domeny, aby utworzyć trzy operacje:
* Zadanie wejściowe;
* Zadanie kontroli motorycznej;
* Zadanie wyjściowe.
W zależności od systemu może to być w porządku. Jeśli jednak przyjrzymy się szczegółom poszczególnej domeny, można odnieść wrażenie, że każde zadanie będzie próbowało zrobić zbyt wiele. A system może mieć problemy ze skalowaniem w miarę rozwoju. Dlatego zamiast tego, na podstawie zidentyfikowanych przez nas zestawów danych, można skłonić się do podziału zadań, takiego jak pokazano na rysunku 6.
Jak widać, w tym wypadku jest pięć operacji:
1. Zadanie kontrolera;
2. Zadanie czujnika;
3. Zadanie odbiorcze;
4. Zadanie motoryczne;
5. Zadanie wyświetlania.
Ten podział będzie znacznie bardziej elastyczny, ponieważ każda operacja ma przydzieloną jedną odpowiedzialność, na której się koncentruje. Na przykład, jeśli klient w najbliższej przyszłości wymaga diagnostyki, możemy dodać takowe zadanie. Jeśli zostanie uwzględnionych więcej czujników, zadanie czujnika może nimi zarządzać. Jeśli potrzebujemy przesłać stan urządzenia za pomocą systemu telemetrii, możemy dodać operację telemetrii i zadanie transmisji danych. Jeśli zaprojektujemy nowy sprzęt, który wykorzystuje taki sam silnik i sterownik, możemy wziąć nasze zadanie silnikowe sterujące nim i wykorzystać je ponownie!
Wypełnianie szczegółów
Po rozczłonkowaniu naszego systemu na bloki i operacje wysokiego poziomu możemy rozłożyć kilka początkowych architektur komunikacyjnych. Na przykład w ostatniej sekcji widzieliśmy, że nasza bazowa architektura będzie miała pięć zadań. Istnieje kilka obszarów, w których widzimy dane, a ich przepływ przekracza granice między operacjami:
* Zadanie czujnika do zadania kontrolera;
* Zadanie odbiorcze do zadania kontrolera;
* Zadanie sterownika do zadania silnikowego;
* Zadanie kontrolera do wyświetlenia zadania.
Przekroczenie granic sugeruje, że operacje te muszą wchodzić w interakcje, aby wymieniać dane lub komunikować się ze sobą w jakiś sposób. W ramach etapu dekompozycji możemy wyróżnić te powiązania i zidentyfikować potencjalne mechanizmy przesyłania zasobów. Jednak często najlepszą praktyką jest odkładanie decyzji architektonicznych tak długo, jak to możliwe. Jeśli więc nie musimy podejmować jej dzisiaj, powinniśmy ją delegować, aż będziemy zmuszeni ją zawiązać. Na tym etapie możemy na przykład przyjrzeć się interakcji zadania czujnika z operacją kontrolera oraz zdać sobie sprawę, że istnieje kilka rozwiązań:
* Użycie kolejki wiadomości, aby przesłać dane o temperaturze do sterownika;
* Użycie współdzielonej lokalizacji w pamięci (z muteksem, aby chronić zasoby).
Jaką opcję powinniśmy zastosować? W tym momencie jeszcze nie musimy przedsięwziąć tej decyzji! W dalszej części projektu mogą pojawić się inne szczegóły, które zasugerują, która z nich jest najlepszym wyborem. Może oba wyjścia są równie poprawne. Dlatego też, zamiast decydować dzisiaj, należy przenieść to ustanowienie w przyszłość.
Wnioski
Jak zaprezentowano w tym artykule, dane, które zostały zidentyfikowane w poprzednich krokach zaczęły naturalnie dyktować sposób, w jaki system rozkładany jest na domeny i zadania. To, jak zasoby chcą przepływać przez system wskazuje, gdzie operacje będą ze sobą współdziałać. Chociaż zwykle istnieje wiele rozwiązań do obsługi interakcji zadań, często ostateczną decyzję odkłada się na późniejszy etap procesu projektowania. Odraczanie jej zazwyczaj pozwala łatwiej zmieniać i rozwijać projekt, a także ugruntowywać rzeczy tylko wtedy, gdy jest to konieczne.
Widzimy powoli, jak z mgły wyłania się obraz architektury oprogramowania. W kolejnym artykule omówiony zostanie czwarty etap, którym jest identyfikacja interfejsów i komponentów systemu.
Źródło: https://www.embedded.com/5-steps-to-designing-an-embedded-software-architecture-step-3/
Fajne? Ranking DIY
