O CZYM PISZEMY?
Wczytywanie...
2022 – podsumowanie roku
Ani się obejrzeliśmy, a kolejny rok wspólnych przygód i pracy za nami! Tradycyjnie więc, kilka słów podsumowania. 2022 to dla itSilesia na pewno rok stabilizacji. Realizowaliśmy przede wszystkim długoterminowe projekty nastawione na stałą współpracę. Bardzo nas to cieszy, bo dzięki temu jesteśmy w stanie budować skuteczne i zgrane zespoły. A co udało nam się stworzyć? Jednym z ważniejszych wydarzeń było niewątpliwie ukończenie projektu BellVR, czyli naszej platformy do szkoleń w wirtualnej rzeczywistości. Pierwsze duże wdrożenie za nami! To jednak nie koniec prac. Platforma jest wciąż ulepszana, opracowujemy nowe, ciekawe rozwiązania takie jak interaktywne testy. Kolejnym ważnym sukcesem było uruchomienie mobilnego showroomu dla Rectus Polska. Projekt, o którym kilkakrotnie pisaliśmy, był dla nas dużym wyzwaniem. Nowatorskie podejście i pomysł Klienta, niezachwiana wiara w sukces, ale też duże oczekiwania i wysoko postawiona poprzeczka przyczyniły się do stworzenia nowoczesnego i interesującego rozwiązania wykorzystującego rozszerzoną rzeczywistość do prezentacji narzędzi pneumatycznych. Raz jeszcze dziękujemy za zaufanie. Intensywne lato zapewnił nam Famur S.A. Odbywające się po dłuższej przerwie Międzynarodowe Targi Energetyczne były jak zwykle okazją do zaprezentowania wielu nowoczesnych technologii. Zespół Famur jak zawsze chciał tego, co najlepsze i nie było mowy o żadnych półśrodkach. Były więc aplikacje AR, Hololensy, VR, platforma szkoleniowa, animacje i totemy dotykowe. Wszystko to, co lubimy najbardziej! Pod koniec roku wraz z kilkoma europejskimi uniwersytetami technicznymi i firmami technologicznymi stworzyliśmy konsorcjum do realizacji projektu, którego celem jest dostarczenie nowoczesnej, skalowalnej i przystającej do dzisiejszych warunków platformy do zdalnego i hybrydowego nauczania na poziomie uczelni wyższych, w szczególności przeznaczonego do prowadzenia zajęć i laboratoriów z zakresu IoT (internet of things). Projekt jest kontynuacją prowadzonego w latach 2016-2019 IOT-OPEN.EU, którego wyniki były wielkim sukcesem (z kursów skorzystało 10 000 studentów ze 130 krajów), jednak wymagają dostosowania do nowych rozwiązań technologicznych zarówno na poziomie hardware'u jak i softwar'u. Projekt jest realizowany w ramach Programu Erasmus+ finansowanego przez Unię Europejską. Nie możemy doczekać się współpracy! To oczywiście nie wszystkie projekty, którymi się zajmowaliśmy, bo w sumie realizowaliśmy ich w 2022 roku aż 43! Dużo działo się także za kulisami. Nieustannie dążący do ulepszeń Łukasz Lipka wysyłał nas na szkolenia, konferencje i webinaria (sam też się nie migał!). Wszystko po to, by lepiej nam się ze sobą pracowało, ale także by skuteczniej realizować projekty. Udało nam się też wielokrotnie spotkać w mniej formalnych okolicznościach na licznych firmowych integracjach. Zdobyliśmy nawet symbolicznie wspólnie jeden górski szczyt, na szczęście w schronisku na wierzchołku czekały ognisko i duże dawki kalorii do uzupełnienia energii! itSilesia wciąż się rozwija. Mamy nadzieję, że kolejny rok przyniesie nam ciekawe projekty, dużo satysfakcji z pracy i zadowolonych Klientów. Tym bardziej że będzie to już… 15 rok na rynku! W nieustająco i dynamicznie zmieniającej się branży it bycie na czasie, znajomość najnowszych trendów i technologii jest nie lada wyzwaniem, które jednak daje mnóstwo satysfakcji i zabawy. Licząc na kolejne tak udane 15 lat wchodzimy w 2023 z nowymi projektami i pomysłami na rozwój.
2 stycznia 2023
Aplikacje targowe – HIT czy KIT?
Czyli czy warto zainwestować w multimedia na stoisku targowym. Planując udział w targach jako wystawca firmy prześcigają się w pomysłach na przyciągnięcie odwiedzających. W końcu celem jest zainteresowanie ofertą, budowanie świadomości marki i nawiązanie relacji biznesowych, które zaowocują w przyszłości. Niezależnie od tego, czy prezentujemy produkt czy usługę, musimy czymś zaciekawić gości targów. Pomysły są różne - bogata oferta gastronomiczna, przyjemne i wygodne miejsca do odpoczynku, gadżety reklamowe, prezenty, zaproszeni goście czy nietypowa aranżacja stoiska. Jednym z ciekawych i dość często stosowanych rozwiązań są też różnego rodzaju aplikacje multimedialne i o nich szerzej w tym wpisie. Na pewno wielokrotnie odwiedzając targi spotkaliście się z totemami czy aplikacjami VR, które zapewniają tak zwany efekt WOW! Często są to proste gry tylko przemycające gdzieś między wierszami logo firmy, innym razem mniej lub bardziej efektowne spacery 3d czy prezentacje. Jaka jest jednak wartość takich aplikacji poza wspomnianym efektem? Właściwie to… niewielka :) Czy zatem warto w ogóle rozważać sięgnięcie po taki produkt, czy od razu można uznać, że to tylko drogi KIT? Z doświadczenia naszych Klientów wynika, że… Warto! Niejednokrotnie mieliśmy okazję wspierać naszych klientów z różnych branż na targach zarówno w Polsce, jak i za granicą. Zajmowaliśmy się tak pojedynczymi aplikacjami, jak i całymi ich systemami obejmującymi przekrojowo ofertę firmy. Przygotowaliśmy w sumie kilkadziesiąt aplikacji w różnych technologiach. Można więc śmiało przyznać, że znamy się na rzeczy. Co więc naszym zdaniem stanowi o dobrej inwestycji i jakie rozwiązania mają sens? Przemyślane. Zaprojektowane z myślą o długofalowym użytkowaniu, a nie jednorazowym wykorzystaniu na targach. Stworzone dla konkretnego użytkownika w firmie. Projektując aplikacje wraz z Klientami zawsze zadajemy sobie pytania - kto, jak i po co będzie używać jej po targach? Co wniesie do Waszego biznesu? Jak możemy ją później rozwijać? Czy końcowym odbiorcą będzie dział handlowy, marketingowy czy szkoleniowy najważniejsze dla nas jest to, by aplikacja miała swoje dalsze życie i była przydatnym narzędziem w codziennej pracy. Takie podejście sprawia, że po pierwsze jesteśmy w stanie przygotować coś naprawdę ciekawego, dzięki zaangażowaniu odpowiedzialnych osób, które wiedzą, że przyda im się to w przyszłości, po drugie, że możemy dany produkt rozwijać dalej, ulepszać i uzupełniać, a koszt nie będzie tylko kwotą "wyrzuconą" jednorazowo, a stworzy wartość dodaną. Doradzając naszym Klientom w ten sposób, odchodząc od niektórych pomysłów i rozwijając inne, wspólnie udało nam się stworzyć kilka HITów, które latami świetnie sprawdzają się w codziennej pracy. I przy okazji robią efekt WOW na targach! Zachęcamy więc do zgłębienia tematu i zaprojektowania czegoś dla własnego biznesu.
16 września 2022
0
Dlaczego używamy AI w VR i AR?
“Dlaczego”, a dokładniej, “do czego”?
AR i VR to pośrednia i bezpośrednia interakcja sprzętu i oprogramowania z człowiekiem. O różnicach między technologiami AR i VR pisaliśmy już na naszym blogu (tutaj). Ponieważ każdy z nas ludzi jest nieco inny, a urządzenia produkowane są masowo, pojawia się problem indywidualnego dostosowywania interakcji między sprzętem a użytkownikiem. Wbrew pozorom nie jest to takie proste, ponieważ klasyczne algorytmy mają bardzo ograniczone zdolności adaptacyjne, nie mówiąc już o sprzęcie. Oczywiście nie mamy tutaj na myśli dostosowywania grubości gąbki zamontowanej w okularach VR do kształtu twarzy :-). Chodzi o interakcję i pracę ze złożonym systemem na poziomie oprogramowania. Idealnie byłoby, gdyby algorytmy dostosowywały się same do użytkownika lub też, wzorem ludzi, potrafiły się uczyć, korzystając z obserwacji i wykazując przy tym dużą tolerancję. W końcu każdy z nas bez problemu potrafi rozpoznać gest kozakiewicza :-) Wskazówka dla młodszych czytelników: poszukajcie, co to oznacza, w Wikipedii, np. tu. W takich sytuacjach, gdzie wymagana jest adaptacja, a informacje nie są jednoznaczne, AI z powodzeniem zastępuje klasyczne algorytmy.
Planując kolejny projekt, zdecydowaliśmy się na wykorzystanie elementów sztucznej inteligencji. Ponieważ integracja AI i VR oraz AR w jednym projekcie nadal należy do rzadkości, postanowiliśmy podzielić się naszymi rozwiązaniami, uwagami i spostrzeżeniami.
Etap entuzjazmuZadanie, które postawiliśmy przed naszym zespołem, wyglądało dość prozaicznie: rozpoznawanie (dynamiczne) gestów wykonywanych przez użytkownika z wykorzystaniem rąk (interesuje nas pojedyncza ręka, przy czym nie ma dla nas znaczenia czy lewa, czy prawa), z minimalnymi opóźnieniami. Dzięki temu nasz system potrafiłby automatycznie weryfikować intencje i poprawność akcji wykonywanych przez użytkownika w wirtualnym świecie. Z naszego punktu widzenia był to element niezbędny w systemach szkoleniowych, w których użytkownicy ćwiczą w wirtualnym świecie interakcję z maszynami (budowlanymi, kopalnianymi czy jakimikolwiek innymi). Na początek skupiliśmy się na typowej obsłudze, czyli: złapanie czegoś (drążka, przełącznika, uchwytu), obrocie w lewo lub w prawo, naciśnięciu (przycisku) i tym podobnych prostych i jednocześnie najczęściej wykonywanych interakcjach manualnych ze sprzętem. Temat nie wyglądał zbyt “groźnie” - w końcu wiele rozwiązań ma wbudowane takie funkcje, tyle że często w mocno okrojonym zakresie.
Etap zaciekawieniaPonieważ założyliśmy dodatkowo, że system musi działać z różnymi urządzeniami AR, VR i innymi oferującymi interakcję za pomocą dłoni (począwszy od Microsoft Hololens, a skończywszy na Leap Motion), ideałem byłoby, gdybyśmy mieli coś w rodzaju HAL (Hardware Abstraction Layer), który powoduje, że nie musimy przygotowywać rozwiązań pod konkretny sprzęt. Z pomocą przyszedł nam MRTK (Mixed Reality Toolkit) od Microsoft, gdzie dane na temat położenia dłoni (palców, stawów) są przekazywane w ujednolicony sposób, niezależnie od tego, jakim sprzętem dysponujemy. Microsoft odrobił lekcję z czasu sterowników dla MS DOS i Windows 95, gdzie przekleństwem deweloperów była konieczność tworzenia szeregu wersji oprogramowania tak, aby działało ono z różnymi konfiguracjami sprzętowymi.
OK - prawdą jest, że niektóre urządzenia nie przekazują kompletu danych, np. ze względu na ograniczenia sprzętowe, niemniej jednak te dane, które są przekazywane nawet przy najbardziej “ograniczonych” urządzeniach, okazały się wystarczające. Prawdziwym problemem stał się natomiast nie tyle brak danych, co raczej ich nadmiar, o czym za chwilę.
MRTK przekazuje dane w postaci położenia oraz obrotu wszystkich elementów składowych pojedynczej dłoni, a jest ich łącznie 25. Dane o obrocie są przekazywane za pomocą kwaternionu. Z grubsza można przyjąć, że odpowiadają one stawom czy też popularnym “kostkom”, a więc miejscom, gdzie zginają się palce. Dane te są przekazywane w postaci bezwzględnej, a zatem położenie i obrót określane są względem początkowego położenia w układzie współrzędnych wirtualnego świata. Więcej o tym rozwiązaniu można poczytać tutaj: https://docs.microsoft.com/en-us/windows/mixed-reality/mrtk-unity/features/input/hand-tracking
Etap powrotu do szkołyNasza analiza gestów ma charakter lokalny, a zatem nie interesuje nas położenie dłoni w przestrzeni. W związku z tym skupiliśmy się na wykorzystaniu informacji o obrocie. Pojawił się tutaj jednak jeden problem: jak zmienić obroty globalne na lokalne i to zapisane w postaci kwaternionów. Krótka analiza dostępnych informacji w literaturze naukowej i popularnej wskazała, że nie powinno to być trudne. A zatem przygotowaliśmy wzory (teoria), opracowaliśmy oprogramowanie wraz z wizualizacją (praktyka) i … połączyliśmy teorię z praktyką, co okazało się wcale nie takie proste: w pierwszej chwili okazało się, że nic nie działa i nikt nie wie dlaczego, a dłonie po przekształceniu wyglądają jak w kiepskim horrorze. Ostatecznie jednak udało się poskromić matematykę.
Strumień danych płynący z MRTK i przekształcony przez nasze oprogramowanie tworzy coś, co nazywamy szeregiem czasowym i to właśnie poddawane jest analizie za pomocą naszego mechanizmu sztucznej inteligencji. Jeśli pojęcie szeregu czasowego jest dla Ciebie mocno abstrakcyjne, to spróbuj wyobrazić sobie kolejne klatki filmu, na którym znajduje się dłoń wykonująca ruch: tutaj jest dokładnie tak samo, tyle że zamiast pikseli mamy dane numeryczne.
Etap panikiRozpoznanie pola bitwy (czyt. artykułów naukowych oraz bieżącego stanu wiedzy) wskazało, że… nikt przed nami tego jeszcze nie robił! Poważnie. Zupełnie nikt. Wszyscy bawiący się w rozpoznawanie gestów wykorzystują strumień wideo i ewentualnie kamery głębi. I na dodatek robią to na klastrach obliczeniowych wykorzystujących zaawansowane karty graficzne (GPU) i mocne procesory. Tymczasem my musimy to zrobić na urządzeniach mobilnych i za pomocą dość ograniczonych danych. Co więcej, nawet po odrzuceniu informacji o położeniu, nasz strumień danych nadal był “ogromny” jak na ograniczone zasoby urządzeń mobilnych AR i VR: 25 kwaternionów (kwaternion to jak nazwa wskazuje 4 dane zmiennoprzecinkowe) dostarczanych do systemu kilkanaście czy kilkadziesiąt razy na sekundę. Może to zatkać nawet wydajny system obliczeniowy, a co dopiero urządzenie klasy telefonu komórkowego.
Etap kombinowaniaDo analizy szeregów czasowych nadaje się logika rozmyta (Fuzzy Logic) oraz regułowe systemy wnioskowania rozmytego (Fuzzy Inference Systems), które są już dość długo obecne w nauce, niemniej jednak ze względu na złożoność obliczeniową i kłopoty implementacyjne są rzadko spotykane w rozwiązaniach przemysłowych. Natomiast wraz z rozwojem uczenia głębokiego (Deep Learning), coraz popularniejsze stały się rekurencyjne sieci neuronowe RNN (Recurrent Neural Networks), a zwłaszcza ich szczególna postać, sieci typu LSTM (Long-Short Term Memory) oraz ich pochodne, tzw Transformers (na moment pisania tego tekstu temat był na tyle nowy, że nie doczekał się jeszcze odpowiednika w polskim słownictwie).
Pierwotnie planowaliśmy zastosowanie wyłącznie złożonej, wielowarstwowej sieci typu LSTM do rozwiązania całości zagadnienia w jednym kroku.
Niestety sieci LSTM mają jednak to do siebie, że wymagają dość dużych zasobów obliczeniowych i to nie tylko na etapie uczenia, ale też na etapie ich używania. Choć są to zasoby zdecydowanie mniejsze niż porównywalne modele w logice rozmytej. Niemniej jednak niezbędna była zaawansowana optymalizacja danych, zmniejszenie ich wymiarowości, a finalnie zupełna zmiana podejścia, o czym przeczytasz poniżej, ponieważ przeniesienie “wprost” nauczonej sieci na platformę mobilną spowodowało nieakceptowalne “lagi”, a “grywalność” naszego rozwiązania umiejscawiała nas w ogonie najgorszych VRek, jakie kiedykolwiek powstały ręką programisty.
Etap euforiiNie wchodząc w przydługie rozważania co do tego, ile czasu, środków i sił przeznaczyliśmy na znalezienie optymalnego rozwiązania, możemy z dumą napisać: tak, to działa! I do tego płynnie. Niemniej jednak konieczne było zastosowanie podejścia hybrydowego: szereg czasowy jest analizowany pod kątem statycznych gestów za pomocą sieci konwolucyjnej, a zatem modelu, który jest znacznie szybszy i wprowadza minimalne opóźnienia, ponieważ korzysta tylko z jednej “klatki” danych. Podobne podejście stosuje się np. do rozpoznawania obiektów w popularnych modelach AI do rozpoznawania obrazów, np. Inception czy Yolo, które również wykorzystują warstwy konwolucyjne. Gdy model bazujący na sieci konwolucyjnej rozpozna charakterystyczny układ dłoni, który potencjalnie może rozpoczynać interesującą nas sekwencję, do akcji wkracza drugi model wykorzystujący LSTM. Pracuje on na bardzo ograniczonym zestawie danych z powodów wydajnościowych. Taka hybryda dobrze sprawdza się na urządzeniach AR i VR (m.in. na Oculus Quest oraz na Hololens 2), które mają ograniczone zasoby obliczeniowe i w trakcie używania sieci korzystają głównie lub tylko z CPU. Aktualne frameworki AI nie udostępniają obliczeń dla GPU zintegrowanych z platformą ARM.
Ciekawostki techniczneW przypadku obu modeli, zarówno konwolucyjnego jak i LSTM, konieczne było uczenie maszynowe. Do tego celu planowaliśmy wykorzystać gotowe frameworki dla komputerów PC, m.in. Keras oraz PyTorch czy Caffee. Finalnie zdecydowaliśmy się na Keras ze względu na jego dojrzałość, sporą liczbę potwierdzonych zastosowań komercyjnych oraz support dla urządzeń mobilnych (m.in. Tensorflow Lite oraz możliwość konwersji modelu do innych formatów). Ponadto Keras po jego integracji z Tensorflow wydaje się być rozwiązaniem, której najstabilniej jest wspierane przez nVidia CUDA, czyli obliczenia z wykorzystaniem GPU.
Przenoszenie nauczonego modelu z platformy uczącej (PC) do rozwiązania docelowego (urządzenia AR/VR) w teorii jest dosyć proste, w praktyce bywa jednak kłopotliwe. W naszym przypadku mieliśmy zasadniczo jedynie dwa możliwe rozwiązania: eksport wyuczonego modelu do TFLite (dedykowany format dla frameworku Tensorflow Lite) lub do otwartego modelu w formacie ONNX (Open Neural Network Exchange). Podejście pierwsze sprawdza się dla platform, dla których dostępny jest Tensorflow Lite, niestety nie dla wszystkich (np. nie jest dostępny dla Hololens). Natomiast sama biblioteka Tensorflow Lite ma tę zaletę, że jest napisana niskopoziomowo w C++ i nawet w przypadku tworzenia aplikacji w językach skryptowych czy interpretowanych rdzeń obliczeniowy działa bezpośrednio na procesorze. To oznacza też, że potrzebne są biblioteki binarne dedykowane dla każdej platformy. W przypadku eksportu i późniejszego importu na urządzenie mobilne w formacie ONNX w większości przypadków możemy korzystać z biblioteki, która jest uniwersalna, ponieważ jest napisana w C#, Java (JavaScript) lub Pythonie i dostępna w postaci kodu źródłowego. Niestety to drugie rozwiązanie jest zdecydowanie bardziej powolne, jak na języki interpretowane przystało. Na dodatek, stosując cały łańcuch deweloperski, trzeba mieć na uwadze, że występuje sporo niekompatybilności pomiędzy poszczególnymi wersjami bibliotek, zarówno po stronie “uczącej” (PC), jak i po stronie “korzystającej” (urządzenia mobilne), jak również między nimi. Przykładowo, uczenie wykonanie za pomocą biblioteki Tensorflow w wersji 2.4.0 lub nowszej nie pozwala na wyeksportowanie w tym samym kodzie naszego modelu do formatu TFLite (tak, TFLite!), ponieważ deweloperzy w Google odpowiedzialni za mechanizm eksportujący najwyraźniej zaspali i nie zdążyli z “dociągnięciem” eksportera i przystosowaniem go do najnowszych wersji bibliotek. Bez problemu natomiast eksportujemy w wersji 2.4.0 do formatu ONNX ale… bez zmiany domyślnych ustawień w tak “zaawansowanej” wersji ONNX nie da się wczytać modelu do oprogramowania dla okularów VR/AR praktycznie w żadnej bibliotece ponieważ… znów trafiamy na niekompatybilność wersji, tym razem na poziomie “zbyt nowych ficzerów” po stronie formatu przenośnego, który nota bene reklamowany jest jako otwarty i uniwersalny. A zatem jak widzicie, mielimy do rozwiązania niezłą zagadkę, a całość projektu na tym etapie bardziej przypominała zadanie ucieczki z Escape Room, niż klasyczną, solidną deweloperkę. Ale nie ukrywamy, że tego typu wyzwania napędzają nasz zespół do pracy.
Etap podiumFinalnie mamy ścieżkę i rozwiązanie, które pozwala na wykonanie uczenia na platformie PC, mamy modele, które z jednej strony zapewniają wysoką (przekraczającą 90%) jakość rozpoznawania gestów, z drugiej strony działają na tyle szybko, że użytkownik nawet nie zdaje sobie sprawy ze złożoności mechanizmów stojących za zaawansowaną analizą jego gestów - całość działa praktycznie w czasie rzeczywistym z opóźnieniami poniżej 100ms (a najczęściej znacznie szybciej).
3 listopada 2021
0
Krótko o tym, co to jest sztuczna inteligencja.
Żyjemy w czasach, gdzie hasło “sztuczna inteligencja”, nazywana w skrócie AI (artificial intelligence), jest bardzo popularne i pojawia się w opisach marketingowych wielu produktów i usług. Ale czym tak naprawdę jest AI? Mówiąc ogólnie, AI powstało jako pomysł na stworzenie sztucznego “myślenia” na wzór mózgu ludzkiego. Na dzień dzisiejszy możemy jednak tylko przypuszczać, jak działa ludzki mózg, głównie na podstawie badań medycznych i obserwacji. Z medycznego punktu widzenia wiemy, że mózg wygląda jak skomplikowana sieć połączeń, w których głównym elementem są neurony, a nasze myśli, pamięć i kreatywność to przepływ impulsów elektrycznych. Ta wiedza dała nadzieję na skonstruowanie analogicznego mózgu w wersji elektronicznej, czy to jako sprzętu, czy też oprogramowania, gdzie neurony zastąpione są elektroniką lub oprogramowaniem. Ponieważ jednak nie mamy stuprocentowej pewności, jak dokładnie działa mózg, wszystkie aktualnie stosowane modele w AI to pewne matematyczne przybliżenia i uproszczenia, służące tylko pewnym określonym zastosowaniom. Z obserwacji wiemy, że da się np. stworzyć rozwiązania, które całkiem dobrze naśladują umysł - potrafią rozpoznawać pismo, obrazy (obiekty), muzykę, emocje, a nawet tworzyć sztukę na podstawie wcześniej nabytych doświadczeń, choć rezultaty tego ostatniego bywają kontrowersyjne. W czym jeszcze AI przypomina ludzki mózg? Cóż.. musi się uczyć! Rozwiązania AI bazują na jednej podstawowej różnicy w stosunku do klasycznych algorytmów: pierwotny produkt to filozoficzna “tabula rasa”, czyli “czysty umysł”, który trzeba najpierw nauczyć. W przypadku złożonych organizmów żywych wiedza pojawia się wraz z rozwojem: zdolność mówienia, samodzielnego poruszania się, nazywania przedmiotów, a w przypadku ludzi i niektórych gatunków zwierząt dochodzą do tego elementy nauki zorganizowanej w przedszkolach, szkołach, uczelniach i w trakcie pracy oraz samodzielnego rozwoju. Analogicznie w większości rozwiązań sztucznej inteligencji - model AI musi najpierw otrzymać określoną wiedzę, najczęściej w postaci przykładów, aby móc później skutecznie funkcjonować jako “dorosły” algorytm. Niektóre z rozwiązań uczą się raz, inne poprawiają swoją wiedzę już w trakcie funkcjonowania (tzw. On-line Learning, czy Reinforced Learning). Jak żywo przypomina to ludzką społeczność: niektórzy kończą swoją edukację i do końca życia pracują w jednej firmie, wykonując jedno zadanie, inni muszą szkolić się przez całe życie, ponieważ ich środowisko pracy zmienia się dynamicznie. Czy AI jest już “mądrzejsza” niż człowiek? Jako ciekawostkę możemy spróbować porównać “moc obliczeniową” mózgu w stosunku do mocy obliczeniowej komputerów. To oczywiście będzie duże uproszczenie, bo natura obu jest zupełnie inna. Po pierwsze, ile neuronów ma przeciętny mózg człowieka? Pierwotnie szacowano, że jest to około 100 miliardów neuronów. Wg ostatnich badań (https://www.verywellmind.com/how-many-neurons-are-in-the-brain-2794889), liczba neuronów w “przeciętnym” ludzkim mózgu to “nieco” mniejsze wartości, o jakieś 14 miliardów, czyli 86 miliardów komórek neuronowych. Dla porównania, mózg muszki owocówki to ok. 100 tysięcy neuronów, myszy 75 milionów neuronów, kota 250 milionów, szympansa 7 miliardów. Ciekawostką jest mózg słonia (znacznie większy od ludzkiego w sensie wymiarów), który ma … 257 miliardów neuronów, czyli zdecydowanie więcej niż mózg człowieka. Z badań medycznych wiemy, że na każdy neuron przypada około 1000 połączeń z sąsiednimi neuronami, czyli tzw synaps, a zatem w przypadku człowieka, łączna liczba połączeń to około 86 bilionów (86 miliardów neuronów * 1000 połączeń). W uproszczeniu możemy przyjąć, że każda z synaps wykonuje jedną “operację”, analogicznie jak jedna instrukcja procesora. Z jaką prędkością działa mózg? W sumie … niedużą. Możemy to określić na bazie interfejsów typu BCI (Brain-Computer Interface), które nie tak dawno pojawiły się jako wynik rozwoju medycznych urządzeń służących do elektroencefalografii (EEG), np opaski produkowane przez Emotiv, dzięki którym możemy sterować komputerem za pomocą fal mózgowych. Oczywiście nie integrują się one bezpośrednio z korą mózgową, tylko mierzą aktywność poprzez analizę sygnałów elektrycznych. Na tej podstawie możemy powiedzieć, że mózg działa ze zmienną prędkością (analogicznie jak tryb Turbo w procesorze) i jest to między 0.5Hz dla tzw. stanu delta (pełny odpoczynek) a około 100Hz dla stanu gamma (stres, pełne napięcie). A zatem maksymalną moc obliczeniową mózgu możemy oszacować jako 8,6 biliarda operacji (8,6*10^15) czyli 8,6 Petaflopa! Mimo względnie powolnego działania mózgu, dzięki zrównolegleniu operacji jest to ogromna liczba. Z Wikipedii (https://en.wikipedia.org/wiki/Supercomputer) dowiemy się, że granica ta została złamana przez superkomputery dopiero w pierwszej dekadzie XXI w. Sytuacja zmieni się wraz z pojawieniem się komputerów kwantowych, które z natury działają równolegle, podobnie jak mózg człowieka, ale na dzień dzisiejszy technologia obliczeń kwantowych nadal jest w powijakach. Podsumowując, na razie AI nie wyprzedziła jeszcze ludzkiego mózgu, ale prawdopodobnie kiedyś to nastąpi. Mówimy tu jednak tylko o prędkości uczenia się, pomijając całą kwestię kreatywności, “wpadania” na pomysły, emocji, itd. AI i urządzenia mobilne Zastosowania sztucznej inteligencji wymagają ogromnych mocy obliczeniowych, szczególnie na etapie tzw. uczenia, i stanowią nie lada wyzwanie, gdy trzeba zintegrować je z rozwiązaniami AR i VR. Urządzenia AR i VR w większości mają bardzo ograniczone zasoby, ponieważ w praktyce są to platformy mobilne bazujące na procesorach ARM, porównywalne wydajnością ze smartfonami. Większość modeli sztucznej inteligencji jest na tyle złożona obliczeniowo (matematycznie), że nie da się ich uczyć na urządzeniach mobilnych. OK - da się, ale potrwa to niewiarygodnie i nieakceptowalnie długo. Zatem w większości przypadków do uczenia modeli stosuje się wydajne komputery PC (klastry) oraz akceleratory graficzne GPU, głównie nVidia CUDA. Następnie wiedza ta jest “eksportowana” do modelu uproszczonego, który “wszczepia się” do oprogramowania lub sprzętu mobilnego AR i VR. W kolejnym wpisie na naszym blogu dowiecie się, jak zintegrowaliśmy AI z VR i AR, jak poradziliśmy sobie z ograniczoną wydajnością urządzeń mobilnych i do czego stosujemy sztuczną inteligencję w AR i VR.
1 września 2021
0
Czym różni się rozszerzona rzeczywistość od rzeczywistości wirtualnej?
Technologie rozszerzonej (AR - Augmented Reality) i wirtualnej (VR - Virtual Reality) rzeczywistości to coś, o czym chyba słyszał już każdy. Ich popularność, wynikająca prawdopodobnie z widowiskowości przy jednocześnie stosunkowo dużej dostępności, nie maleje. Technologie te wciąż są rozwijane i ulepszane, znajdowane są nowe zastosowania - jedne służące rozrywce, inne nauce, a jeszcze inne biznesowi. Pomimo że są to dwie różne technologie, wymieniane są często obok siebie, a dla osób nie pracujących z nimi na co dzień, różnica może nie być oczywista. Dlatego postanowiliśmy odpowiedzieć na pytanie - czym różni się rozszerzona rzeczywistość od rzeczywistości wirtualnej? Najprościej rzecz ujmując, AR to technologia nakładająca wirtualne elementy na rzeczywisty obraz, natomiast VR to rzeczywistość w całości wykreowana wirtualnie. Zarówno AR, jak i VR pozwalają na interakcję z wykreowanym światem w czasie rzeczywistym. Jak działa AR? Do wykreowania AR potrzebne jest urządzenie - telefon, tablet, okulary - z kamerą i odpowiednią aplikacją. Dzięki aplikacji kamera rozpoznaje wcześniej opracowany obiekt lub specjalny znacznik (marker) i nakłada na niego wykreowany, przypisany do niego obraz - grafikę. Taka dodatkowa grafika - właśnie rozszerzająca rzeczywistość - może pokazywać np. wnętrze obiektu, na który skierujemy kamerę, dodatkowe elementy, które możemy do niego dołączyć lub zupełnie abstrakcyjną rzecz np. stworka w grach dla dzieci. Jak to wygląda w przypadku VR? VR zupełnie oddziela nas od świata rzeczywistego. Aby z niej skorzystać, potrzebujemy specjalnych gogli (np. Oculus Quest 2), które nie tyle nakładają jakiś obraz na rzeczywiste tło, a wyświetlają nam w 100% wygenerowaną komputerowo, inną rzeczywistość. Nie mamy tu odniesienia do otoczenia, znajdujemy się w zupełnie innym świecie, niezwiązanym z prawdziwym miejscem, w którym się znajdujemy fizycznie. Stąd określenie “zanurzania się” w wirtualną rzeczywistość, ponieważ wchodzimy w nią cali, możemy się po niej rozglądać, wchodzić z nią w interakcję np. poprzez dotyk itd. Skoro już wiemy, czym różnią się te dwie technologie, pojawia się pytanie - jak możemy je wykorzystać? Możliwości jest bardzo wiele. Rozszerzona rzeczywistość jest świetnym narzędziem marketingowym, doskonale sprawdzi się również na ekspozycjach oraz jako wsparcie serwisowe. Pozwala na poszerzenie informacji zawartych np. w broszurach reklamowych poprzez dodanie statycznych lub ruchomych grafik, prezentacji, wykresów lub po prostu jako ciekawy, przykuwający uwagę gadżet np. gra. Dzięki możliwości nałożenia obrazu na istniejący obiekt pozwoli np. na zobrazowanie działania zabytkowej maszyny lub pokazanie wnętrza ciekawego eksponatu. W trakcie prac serwisowych rozszerzona rzeczywistość może ułatwić pracę nawet niedoświadczonemu pracownikowi podsuwając instrukcję krok po kroku lub nawet prezentację koniecznych do wykonania czynności. W itSilesia mieliśmy możliwość zastosowania AR w wielu projektach. W Muzeum Etnograficznym w Chorzowie możecie dzięki niej zobaczyć, jak działa starodawna młocarnia oraz jak wyglądał.. chlewik w XIX w. Elementy AR towarzyszą nam również przez całe zwiedzanie w formie gry terenowej. W aplikacji stworzonej dla Davis Polska możecie sprawdzić, jak wybrany materiał prezentuje się na gotowym meblu, a aplikacja predictive maintenance dla FAMUR S.A. pozwala śledzić aktualne parametry podzespołów kombajnu górniczego. AR to także dobre rozwiązanie na aplikację edukacyjną dla dzieci - w taki sposób zostało wykorzystane w "Atlasie" dla Domu Współpracy Polsko-Niemieckiej prezentującym historię Śląska. Wirtualna rzeczywistość (VR) znajdzie zastosowanie wszędzie tam, gdzie mamy ograniczoną możliwość kontaktu z prawdziwym obiektem - bo jest niedostępny (np. kosmos, nieistniejąca budowla, ale też bardzo drogi, specjalistyczny sprzęt) lub niebezpieczny (np. wnętrze wulkanu, ale również miejsca o ograniczonym dostępie w zakładach produkcyjnych), albo przeciwnie - bardzo delikatny lub dostępny w ograniczonych ilościach - tu mamy na myśli np. rzadkie okazy fauny/flory czy obiekty muzealne, ale również chociażby ludzkie ciało. Dzięki możliwości wygenerowania tych obiektów wirtualnie, zyskujemy szansę na ich obejrzenie oraz na interakcję - przeprowadzenie symulacji zabiegu medycznego czy serwisu maszyny lub np. akcji ratowniczej w trudnych warunkach. Przykładem mogą być tu stworzone dla Muzeum Górnictwa Węglowego w Zabrzu aplikacje prezentujące kilka rodzajów katastrof górniczych i przyczyn ich powstawania. Jako widz znajdujemy się wewnątrz chodnika górniczego i możemy obserwować kolejno - zalanie, wybuch metanu, tąpnięcie oraz pożar. Najbardziej rozwiniętym projektem VR, jaki do tej pory zrealizowaliśmy, jest platforma do prowadzenia szkoleń w VR, z której korzysta między innymi producent maszyn górniczych FAMUR S.A. Platforma umożliwia pełne przeszkolenie pracowników w danej dziedzinie - więcej na ten temat można przeczytać TUTAJ. TUTAJ. Jak widać zastosowań obu opisanych technologii już teraz jest bardzo dużo, a wszystko wskazuje na to, że będą pojawiać się kolejne. Ich możliwości są właściwie nieograniczone niczym poza wyobraźnią oraz.. terminami naszych grafików i programistów :) Gotowi na coś bardziej zaawansowanego? W kolejnym wpisie dowiecie się, jak integrujemy technologie VR i AR oraz sztuczną inteligencję.
4 sierpnia 2021
0
Podsumowanie roku 2020 w itSilesia
Ukończenie prototypu naszego produktu BELL VR Dzięki wsparciu uzyskanemu w ramach konkursu Design dla przedsiębiorców udało nam się zrealizować prototyp narzędzia do prowadzenia szkoleń i prezentacji w VR. Platforma umożliwia zaprezentowanie grupie użytkowników urządzenia, sposobu działania, czynności serwisowych, Znajduje również zastosowanie w procesie sprzedażowym dzięki możliwości prezentacji unikalnych cech produktu. Więcej informacji dostępnych pod linkiem - https://itsilesia.com/platforma-szkoleniowo-prezentacyjna-vr/ Heroo Mobile Heroo Mobile to sieć telefonii komórkowej przeznaczona dla dzieci. Jednak oprócz dostępu do sygnału oferuje swoim klientom dużo więcej - naukę rozsądnego korzystania ze smartphonów. Dzięki zrealizowanym przez nas aplikacjom przeznaczonym dla dziecka i dla jego opiekuna umożliwiamy najmłodszym użytkownikom oswojenie się z technologią, naukę bezpiecznego korzystania z telefonu i rozsądnego podziału czasu między zajęcia online i offline oraz zachętę do spędzania czasu poza siecią, a opiekunom kontrolę i wsparcie dzieci. Dla nas projekt był wyzwaniem, w ramach którego musieliśmy mocno zacieśnić współpracę naszych działów web i 3D i zrealizować go w bardzo krótkim czasie. Pierwsze wersje aplikacji dostępne są w sklepach Adroid i iOS, a nad kolejnymi prace już wkrótce. Udrink Nasz pierwszy pełnoprawny produkt. Aplikacja umożliwiająca składanie zamówień w restauracjach, klubach i pubach za pomocą własnego telefonu komórkowego i umożliwiająca zarządzanie zamówieniami po stronie baru. Za nami już kilka wdrożeń w całej Polsce, między innymi we Wrocławiu i w Gdańsku! Trzymamy kciuki, by gastronomia miała się dobrze w 2021 roku! Więcej informacji znajdziecie na stronie http://udrink.pl/ Stali klienci - kontynuacje projektów Rok 2020 to także kontynuacja współpracy z naszymi stałymi klientami. Mimo niepewnej sytuacji na rynku firmy te podjęły decyzje o realizacji projektów na dużą skalę lub rozbudowywaniu produktów, które zrealizowaliśmy wspólnie wcześniej. Dla Famur S.A. realizujemy projekt z zakresu prezentacji maszyn górniczych w wirtualnej rzeczywistości. Projekt jest kontynuacją współpracy z firmą, którą mieliśmy możliwość wspierać na kilku ważnych branżowych wydarzeniach ostatnich lat. Dla Rectus Polska stworzyliśmy aplikację, o której… już niedługo :) Do aplikacji stworzonych we wcześniejszych latach dla Davis Polska oraz TDJ Estate i jej spółek dodawaliśmy natomiast nowe elementy. Cieszymy się, że nasi klienci wracają do nas z nowymi pomysłami i wspólnie możemy rozwijać ciekawe produkty. NCBR, stworzenie działu R&D Przez dużą część roku nasz zespół pracował nad wnioskiem o dofinansowanie prac badawczo - rozwojowych z Narodowego Centrum Badań i Rozwoju. 8 lipca znaleźliśmy się na PIERWSZYM MIEJSCU (!) listy projektów rekomendowanych do otrzymania środków. Z początkiem grudnia rozpoczęły się prace w ramach projektu, które potrwają przez najbliższe kilkanaście miesięcy. Mamy nadzieję, że uda nam się osiągnąć wszystkie zaplanowane cele. O postępach prac będziemy na pewno informować na naszej stronie i w mediach społecznościowych. Infotower i … drugi projekt B+R W tym roku rozpoczęliśmy również współpracę z Infotower, dla której realizujemy jako podwykonawca prace programistyczne w projekcie badawczo-rozwojowym również finansowanym przez NCBR. To duży i ciekawy projekt dla branży turystyczno - rozrywkowej i dla użytkowników indywidualnych. Cieszymy się i dziękujemy za powierzenie nam tak odpowiedzialnego zadania! Praca zdalna, praktyki i nowi pracownicy Większą część roku przepracowaliśmy zdalnie. Na pewno brakuje nam kawy we wspólnej kuchni, jednak odkryliśmy też, że potrafimy współpracować na odległość. To, co trzeba podkreślić, to fakt, że na pewno zwiększyło to u wszystkich zdolności komunikacyjne! :) Pokazuje to również, że dzięki narzędziom, których dostarcza nasza branża, można naprawdę poprawić jakość pracy. Mimo utrudnień związanych z panującą epidemią udało nam się również przeprowadzić coroczne praktyki zawodowe. Po kilku tygodniach wytężonej nauki i pracy wyłoniliśmy kandydatów i nasz zespół powiększył się o cztery nowe osoby. Liczymy, że zostaniecie z nami na dłużej. Co przyniesie 2021? W itSilesia mamy naprawdę wiele planów… Trzymamy kciuki, by wszystkie mogły być realizowane przede wszystkim w bardziej spokojnych okolicznościach. Jeśli chcecie być na bieżąco z tym, co się u nas dzieje, zapraszamy do śledzenia naszych profili w mediach społecznościowych FB https://www.facebook.com/itSilesia.Software Linkedin https: https://www.linkedin.com/company/itsilesia/
27 stycznia 2021
0
How to debug your Unity application?
Sometimes things happen or do not happen differently than we expect it. That often requires a thorough investigation of the causality and flow of our code. The first move would normally be to throw a Debug.Log somewhere, where we expect our problem to happen. However, what if it is not enough?
Another parameter in Debug.Log
The problem: You got a number of objects with the same name that would be normally hard to distinguish. The solution: The first thing to beef up the log statement is to add an extra parameter to our call. As per documentation:public static void Log(object message, Object context); If you pass a GameObject or Component as the optional context argument, Unity momentarily highlights that object in the Hierarchy window when you click the log message in the Console.Now what does this mean? Let's say our debug code looks like this: [csharp] public class FindMe : MonoBehaviour { void Start() { Debug.Log("ヘ(・ω| Where am I?", this); } } [/csharp] Then by simply clicking on the log, the corresponding object will be highlighted. In this case a
Component
was passed (this refers to the class we're at, which eventually inherits after Component). Similarly, any GameObject
could be passed.
From the official description, it could seem that only objects that are in the Hierarchy window could be highlighted this way. Though the method header indicates that any UnityEngine.Object
could be used. This is exactly the case and we can use it to locate anything that has an instance ID. It includes but is not limited to classes like: Material
, ScriptableObject
, and Mesh
.
Bonus fact: We can use EditorGUIUtility.PingObject
to use the highlight functionality without writing logs. Link
Making the logs pop
The problem: You need to log a lot of things and while viewing the data and categorize it quickly at a glance. Using the search box and collapsing the logs do not work as well as the order of messages is important and a value that is searched for is not that obvious. The solution: Spice up your logs visually. Log statements can take in Rich Text tags. Differentiating by color is way faster for eyes than reading each line. In the following example, if a slightly concerning value is shown, then the color of the value changes. This is easily noticed almost immediately.Stopping the flow
The problem: The special case denoted by color is not understood enough once it passed. You need to inspect the scene and/or look at the exact execution in code. The solution: Firstly, the more known approach. Setting up a breakpoint. Unity and Visual Studio work quite well together for this purpose. If on the Visual Studio side for basic debugging, link to the official docs. Getting it working in Unity is quite simple:- Click to the left of the line of code you want the break to happen at.
- Attach to Unity.
- Press Play.
- The breakpoints is hit. From there Visual Studio allows to inspect Local values, investigate the Call Stack and use many other Visual Studio debugging tools.
Debug.Break
is your friend. It acts as if the pause button was pressed, except it is exactly at the desired line in code. Then the state of the scene can be fully explored. Alternatively use a debug using Debug.LogError
while enabling the option to pause on error.
Adjusting the console window
The problem: There are a myriad types of data that may need to be displayed. Not every type fits neatly in the limited space provided by the log entry. The solution: Look into the options of the Console window and select as necessary. Additionally, timestamps can be enabled/disabled.What did I just build?
The problem: Build completed with a result of 'Succeeded' is not enough information about what has just happened. Especially when looking to slim down the size of the app. The solution: Console window options, then "Open Editor Log". and select as necessary. A text file opens. Starting from section of Build Report there is a detailed breakdown of what assets went in and how much space do they take up, conveniently in descending order.Logging based on preprocessor directives
The problem: There is a system, that is so vital to the whole app that every time it is being interacted with, you have a set of key debug statements that you want to know about. However it is too many to simply comment and uncomment every time. The solution: Creating a static class that is active with a custom directive will call the corresponding log method with custom prefix. To quickly turn that off have another class with exactly the same name and methods that is active with negation of the directive. Then inEdit > Project Settings > Player > Other Settings > Scripting Define Symbols
add the symbol that is used to filter this particular logger. The following example assumes naming conventions for a networking module selective logging:
[csharp]
using UnityEngine;
#if NET_LOGGER && (UNITY_EDITOR || DEVELOPMENT_BUILD)
public static class DebugNet {
public static void Log(string message) {
Debug.Log($"[NET] {message}");
}
public static void LogWarning(string message) {
Debug.LogWarning($"[NET] {message}");
}
public static void LogError(string message) {
Debug.LogError($"[NET] {message}");
}
}
#else
public static class DebugNet {
public static void Log(string message) { }
public static void LogWarning(string message) { }
public static void LogError(string message) { }
}
#endif
[/csharp]
To ensure that the logs never end up in the final build, display will also be dependent on the build being a development variant or in the editor. To learn more about directives that Unity provides, have a look in the documentation.
A potential drawback of this solution is that it does leave debug statements in the main code flow (even if you can hide them in a way that minimizes impact on performance). On the other hand, it is like leaving a comment in code of what is expected to have happened at that particular point in time.
Numbers not enough?
The problem: There is a lot of data. This data does not make much sense when viewed as numbers. Maybe it denotes some direction, or area, or it is a number/string, but it's very important to know at a glance its current value and what object it's attached to. The solution: Depending on the needs, there are many approaches to debug by drawing things on the screen.Debug class
The quickest way to draw a line is via the Debug class. From documentation (line, ray):public static void DrawLine(Vector3 start, Vector3 end, Color color = Color.white, float duration = 0.0f, bool depthTest = true); public static void DrawRay(Vector3 start, Vector3 dir, Color color = Color.white, float duration = 0.0f, bool depthTest = true);Those two methods are especially useful in debugging things like raycast. The simple example would be to see how your object sees the world in front of them. This could highlight many possible issues that arise from improper layer setups or wrongly defined raycast source and direction. [csharp] [SerializeField] float hitDist = 1000; void Update() { Ray ray = new Ray(transform.position, transform.forward); RaycastHit hit; if (Physics.Raycast(ray, out hit, hitDist)) { Debug.DrawLine(ray.origin, hit.point, Color.green); //Do your thing on hit } else { Debug.DrawLine(ray.origin, ray.GetPoint(hitDist), Color.red); } } [/csharp] This snippet shows how to visualize your raycast. Do not that if the point is not hit,
DrawLine
is used also. This is so that we don't draw a line of infinite length, but the actual distance that is being tested via raycast. The film shows behavior with the aforementioned code:
Gizmos and Handles
If you need more custom shapes to be displayed than simple lines then the Gizmos context is your friend. It can help you draw all sorts of basic shapes, as well as custom meshes. In the following example bounds are being visualized in a similar way a box collider might do it. [csharp] public class BoundingBox : MonoBehaviour { [SerializeField] Bounds bounds; [SerializeField] bool filled; void OnDrawGizmosSelected() { Gizmos.matrix = transform.localToWorldMatrix; if (filled) { Gizmos.DrawCube(bounds.center, bounds.size); } Gizmos.DrawWireCube(bounds.center, bounds.size); } } [/csharp] To ensure the coordinates of the bounds drawn are responsive to the transform component, a matrix that will transform each drawn point from local space to world space is set. There is also a Handles class is generally used to make Editor tools, as it has got mostly methods that return a value if the user modifies something. However, for debugging it has one major advantage that Gizmos doesn't have. It has a handy way to add labels to your objects (documentation).public static void Label(Vector3 position, string text, GUIStyle style);[csharp] void OnDrawGizmos() { Handles.Label(transform.position, $"{gameObject.name} {transform.position.ToString()}", EditorStyles.miniButton); } [/csharp] This snippet demonstrates how to draw a temporary label over the object. It is drawn at the position of the object and displays the object name and position. This can be used to display all kinds of data that you need to know the current state of some variable, and to which object they correspond to with a single glance. As the label is white and thin by default, a style was applied to rectify that. For quick setup that will be visible with any background EditorStyles class was used that houses a button type display. Note that Gizmos methods can be called only in functions
OnDrawGizmos
, OnDrawGizmosSelected
and those with proper attribute usage. If you try it in any other place, it will not work. This means that Gizmos are specific to Components. If you need to draw things from an Editor window, then Handles are the only option.
In conclusion...
As a final note sometimes to aid with the debug code some third party tools or scripts are needed. One should always be mindful of adding robust plugins, as it may have many functionalities that are simply not needed for the project, and might even hinder it. That being said, now you have hopefully learned some techniques that you might be considering to use when tackling a new bug or aiming to gain a greater understanding of your system. Keep in mind though that a solution should always be tailored to the problem and using a shiny new technique is usually not the way to go. Always take a step back to consider what is actually needed.22 stycznia 2021
0
Aplikacja VR – po co i dla kogo?
Wirtualna rzeczywistość (VR) od kilku lat szturmem zdobywa przestrzenie targowe, stoiska promocyjne, korytarze muzealne i liczne eventy. Kiedy w jakimś pokoju konferencyjnym pada hasło: “pokażmy się jako nowoczesna firma”, jestem się w stanie założyć, że jedną z pięciu pierwszych wymienionych metod będzie: “zróbmy coś w VR”. Koncepcja jest słuszna - VR jest nowoczesną, stale rozwijającą się technologią, w dodatku bardzo atrakcyjną wizualnie i wciąż - mimo wszystko - nie tak popularną i dostępną, a sposobów na jego wykorzystanie jest bardzo dużo. No właśnie - tu dochodzimy do sedna. Czy chcąc się pokazać jako nowoczesna firma, powinniśmy zainwestować w zakup/wynajem sprzętu do VR i uruchomić na nim cokolwiek? Czy jakakolwiek aplikacja VR, to właśnie to, o co nam chodzi? Jaki ma być rezultat poza przyciągnięciem kilku osób, które jeszcze nie miały na głowie hełmu, i zapewnienia im chwilowej rozrywki? Jakie realne korzyści może nam przynieść taka inwestycja i właściwie do czego ją wykorzystać? Moim zdaniem błędem jest nie wykorzystanie pełnego potencjału, jaki daje nam takie środowisko. Oczywiście nie ma niczego złego w odrobinie zwykłej rozrywki. Jednak jaka jest realna szansa, że po chwili zabawy, jednorazowym evencie zostanie z tego coś więcej, coś na dłużej, jakaś wymierna korzyść? Można przyjąć, że niewielka. Jak więc wykorzystać pełny potencjał, jaki daje nam wirtualna rzeczywistość? Opcji jest kilka. Punktem wyjścia powinno być podstawowe pytanie - do czego jeszcze może się nam to przydać? Jak z aplikacji VR nawet na jeden event zrobić coś, co zostanie w firmie na dłużej i będzie wykorzystywane w jej codziennym funkcjonowaniu? Jak zamiast jednorazowej zabawki zyskać użyteczne narzędzie? Nie bez znaczenia pozostaje fakt, że obecnie urządzenia do VR - hełmy, gogle - są lekkie, nie mają okablowania i nie wymagają żadnych podłączeń - dzięki temu są mobilne i łatwe w użyciu. Czy zdecydujemy się na aplikację sprzedażową, dzięki której będziemy mogli pokazywać klientom produkty np. w reprezentacji ich naturalnego środowiska lub w różnych konfiguracjach, czy na aplikację demonstracyjną, dzięki której będziemy mogli własnym pracownikom lub pracownikom klienta zademonstrować działanie jakiegoś urządzenia, czy wreszcie na pełną aplikację treningowo - szkoleniową, dzięki której będziemy mogli przeprowadzić właściwie kompleksowe szkolenie pracownika bez konieczności uruchamiania np. ciężkiego sprzętu lub organizacji całego dnia szkoleń w odległym miejscu - będą to rozwiązania, które można rozwijać i które przynoszą realną korzyść. Obecnie wielu z nas przeniosło się z pracą do internetu, wiele firm działa online. Można przypuszczać, że rynek VR, który rozwijał się dynamicznie jeszcze przed pandemią, będzie w najbliższych miesiącach tematem wielu rozmów, ponieważ jest idealnym narzędziem, by ułatwić nam taką pracę i sprawić, by część koniecznych działań była bezpieczniejsza. Właśnie VR zdaje się być naturalnym, kolejnym krokiem i istnieje wiele sposobów, by mądrze go wykorzystać. Jeśli interesuje Cię praktyczne wykorzystanie VR, możesz sprawdzić, jak to robimy między innymi tu: https://itsilesia.com/casestudies/szkolenia-w-wirtualnej-rzeczywistosci-i-wsparcie-serwisu-w-rzeczywistosci-rozszerzonej/
2 lipca 2020
0
My feelings after the first use of Gatsby
How it started Recently, I was asked to create a static site for the client (kind of landing page) in a really short time with the deadline approaching. The first thing I had to do was to choose a technology for that project. Of course, first what occurred to me was to make a website in plain HTML and CSS and add some JavaScript files if needed for the staff like subscribing for the newsletter or just performing some dynamic actions on the pages. One of the project requirements was a good SEO. During the research, I was coming across a lot of articles about better SEO by use of Next.js (Server Side Rendering in React) and GatsbyJS. I have heard a little about Gatsby known as a static page generator before but I decided to try this tool out. First impression After installing Gatsby on my machine and creating a new project I got the project scaffold with all the required directories and files ready to start. The cool thing is that you can just start adding new views by creating React components inside pages directory. Gatsby handles the routing under the hood, so you do not have to prepare all the routes like in create-react-app. If some internal or external script or links are about to be added to head or body tags of our output index.html it can be done inside html.js file. We also have access to application internal cache files which are put inside .cache directory. However, one of the most important and useful modules generated is gatsby-config.js. That file consists of Gatsby plugins that are node.js packages used for better modularizing and customizing Gatsby projects. It acts as a webpack-config.js in create-react-app. Plugins can be used for different purposes like support Google Analytics tools, compiling e.g. Sass files into the regular CSS or configure any third-party libraries for proper usage. In my case, I used styled-components for creating and styling my UI. To make sure the library has server-side rendering support I was supposed to install gatsby-plugin-styled-components. The last step was to add it to the list of plugins in gatsby-config.js file. [code] const path = require('path') module.exports = { plugins: [ 'gatsby-plugin-styled-components' ] } [/code] Server Side Rendering Regarding SEO, the first thing that should be explained is how Gatsby handles its files during the build process. As we can see on the picture above Gatsby takes all the files and performs Server Side Rendering build. What does it exactly mean? Unlike for example Next.js which renders pages on the server and then sends them to the client, Gatsby does it during the build time. In other words, Gatsby prepares all the files that are supposed to be sent to the server. For instance, all the routes available in the application are set during the build process. Suppose that we have our application hosted on the server. If we visit mysite.com/about, we get about.html rendered during the build time and corresponding JavaScript file adding reactivity for that page. The web browser does not do any additional work, because a desired html has been rendered and sent to the server before. What is more, if we want to fetch some data from the external API or CMS, it is also performed at the build time using GraphQL. So, after the server reads all the files it is impossible to fetch additional data from the CMS until we build our application with a new content. However, fetching data not related to CMS can be done directly by the JavaScript included in response using fetch or axios. Because Gatsby pages are pre-rendered and sent to the server, the site content is immediately available for the search engine crawlers, so we get a better page indexing. There is a bunch of plugins enhancing the SEO on Gatsby pages available on npm. When Gatsby is a good choice? When we build a website with content that is not supposed to change very often, we can choose Gatsby for that purpose and we do not have to worry about scaling. On the other hand, Next.js is a better option when data is about to be changed more frequently. Suppose, that we have an application with hundreds of pages. Every time data taken from the CMS change, it triggers auto build and deploy the project again. While using Gatsby, it may take so long. In comparison, Next.js can at all times dynamically request new data from the server. If we, for example host our static Gatsby website on the S3 server, we get an access to the history of the website data. It can be really helpful if the current version includes some bugs and we want to deploy the latest stable version. Conclusion I don’t see Gatsby only as static page generator. I would rather name it as a progressive web application generator. Gatsby sites are optimized to be highly performant, have very good caching and indexing with a possibility of including reactivity to them. So, I can say it is nothing more than regular React application with some additional features. I really like how fast you can start working with Gatsby with project configuration out of the box. I am sure I will choose Gatsby for any of my further projects.
23 stycznia 2020
0
How NOT to Write Code in React JS
React, Vue, Angular, Ember, Backbone, Polymer, Aurelia, Meteor, Mithril.js, Preact... There are many super fancy Javascript frameworks nowadays. We can write anything we want, it's comfortable, easy to understand, although difficult to master. After few lines of code, even after writing few small applications you may think that no matter what you write, these frameworks will do the job. Yeah, what you see above is the iconic series Star Wars and Chewbacca being very skeptical about Han's idea. In the programming universe, you should have this Chewbacca in your mind and this Chewbacca should always be skeptical about your code. It's very important to write your code carefully and thoughtfully no matter what framework you are using. I know it looks that these frameworks do everything for you and you don't have to worry about anything but it's not entirely true. Buckle up, in this article we are going to go through the most common mistakes done in React (and probably other similar frameworks/libraries). I am probably one of the most reliable people to talk about it because I used to make some of these mistakes for a loooong time. And probably I'm still doing some of them.
New feature? Yeah.. One component is enough.
Nope. Nine times out of ten it won't be enough. Imagine that, in your application, you have a list of board games with some simple filtering controls over the table. You know, choosing a price, an age or a type. At the beginning it looks like it's enough to create a BoardGamesList component and put all of the logic inside. You may think that it doesn't have sense to create a separate BoardGameRow and BoardGamesFilters components. Or even PriceFilter, AgeFilter and BoardGameTypeFilter. "It's just a few lines! I don't have time to create all these components with so few lines of code." It's just a few lines for now. But it's very likely that during next year your client will be requiring few more filter options, some fancy icons, 5 ways of ordering the game and 10 ways to display the game row depending on something. Trust me, in my programming life I have experienced too many components which were small at the beginning and after a year it was a massive, uncontrollable piece of sh.. component. Seriously, if you take a few moments and divide it functionally at the beginning, it'll be much easier to work with this component in future. For you. For your colleagues. And even if it stays so small, it'll be easier to find what you need if you rationally divided it into separate React components. Then your work will look like this:- Hey, we have some weird bug while filtering board games by age. Can you do something about it? - Yeah, I know exactly where to find it. It's in PriceFilter.js and it's so small that I will need half an hour to fix it! - Great, I think you should get a pay rise!It won't necessarily end this way. But there is a better chance if you properly divide your code into components. And look - I don't even say a word about reusability which is the main reason we use components. But this is a topic for a separate article.
setState? Pff.. I don't have to read the documentation.
If you decided to use React state in your components, you should know that its main function, setState, is asynchronous. What means you can't just put some important code depending on your state just after setState execution. Let's look at this case: [js] this.setState({isItWorking: true}); console.log(this.state.isItWorking); // returns false, WHY?! [/js] Method setState is asynchronous what means it needs few moments to properly set the data you passed. How to handle this problem correctly? The most common way is to pass a callback function as a second parameter which will be executed when the data is passed to the state. [js] this.setState({isItWorking: true}, () => { console.log(this.state.isItWorking); // and now it returns true }); [/js] Sometimes you have to do some consecutive operations on your state. Because of setState asynchrony, you may receive unexpected results. [js] // this.state.name = 'STAR WARS: ' this.setState({name: this.state.name + 'THE RISE OF '}); this.setState({url: this.state.name + 'SKYWALKER'}); [/js] Unfortunately, you won't finish with the real name of episode IX - STAR WARS: THE RISE OF SKYWALKER. Probably you will get partially filled title like STAR WARS: SKYWALKER. It would be a nice title but it's not what we wanted because the second setState has been overwritten by the last one. To fix it you can use one more time the callback technique but there is another way to handle this case. Instead of passing a new object you can pass a function which returns an object. What's the difference? This function's first parameter is the current "version" of state so you will always work on the updated state. [js] // this.state.name = 'STAR WARS: ' this.setState(state => ({name: state.name + 'THE RISE OF '})); this.setState(state => ({name: state.name + 'SKYWALKER'})); [/js] If it's not enough for you and you want to know how setState works internally it'll be a smart choice to read an article from Redux co-author, Dan Abramov: How Does setState Know What to Do?Hey! Why this.props is undefined?
This mistake is still very common, no matter that arrow functions are one of the main features of ES6 specification. [js] handleFieldChange() { console.log(this.props); // return undefined } [/js] Why? I am inside the component, I should have access to my props! Unfortunately not. This function has its own this (different than component's this) and if you want to use the standard function you should consider binding this with .bind(this) or not so beautiful const self = this before the function. Much easier and a simply better option is to use ES6 arrow functions. [js] handleFieldChange = () => { console.log(this.props); // YEAH! It returns my props! } [/js] Arrow function uses something what is called lexical scoping. In a simple way - it uses this from the code containing arrow function - in our case, the component. That's it. No more bindings, no more awful selves, no more unexpectedly missing variables. Arrow functions are also very useful if you need to propagate a few functions. For example, you need a setup function which takes some important parameters and then returns the generic handler function. [js] handleFieldChange = fieldName => value => { this.setState({[fieldName]: value}); // [fieldName] - for your knowledge, it's dynamic key name, it'll take the name you pass in fieldName variable } [/js] This is a very generic way to create a function that receives field name and then returns the generic handler function for, let's say, an input element. And if you execute it like that.. [js] <Input onChange={this.handleFieldChange('description')} />; [/js] ..your input will have this classic function assigned to onChange event: [js] handleFieldChange = value => { this.setState({description: value}); } [/js] You should also know that you can fully omit curly braces if you have something very short to return. [js] getParsedValue = value => parseInt(value, 10); [/js] In my opinion, in most cases you should avoid it because it can be difficult to read it. On the other hand, in simple cases like above, it'll save you a few lines. But you should be careful doing it. Let's say, I have single object to return. I decide to return it in one line because it's a really short line. [js] getSomeObject = value => {id: value}; [/js] Oh yeah, you may think that based on the previous code example it should definitely work. But it's not and it's quite easy to explain. In this case, the system thinks that you use standard arrow function and these curly braces are just the beginning and the end of the function. If you really want to return an object in one line you should use this syntax: [js] getSomeObject = value => ({id: value}); [/js] In this case, the returned object is contained in the brackets and it works like intended. Personally, I don't like using one line functions but it's a very nice way to pass a short code to functions like map or filter. Clean, easy to read and it's included in one line. [js] someCrazyArray.map(element => element.value).filter(value => value.active); [/js] Okaaaaaaay, quite a lot of info about simple arrow functions but I think it's valuable if you didn't know about it. Let's go to the next one of the ReactJS mistakes!The browser has crashed.. Why?
I can't even count how many times I or my buddies were struggling with some weird browser crash or inability to do any action in the application. In many cases, the reason is an infinite loop. I suppose there are billions of ways to get the infinite loop but in React the most common is componentWillReceiveProps or any other React lifecycle method. ComponentWillReceiveProps is currently deprecated but I will focus on this one because there are plenty of React applications still using it and for me most of these bugs happened in this very important lifecycle method. I have multiple examples in my mind which can help visualize the problem but for the purposes of this article I will present this use-case based on the board games example:Every time a user changes the age, the application should load board games for the chosen age.
[js] componentWillReceiveProps(nextProps) { if (nextProps.age) { this.loadBoardGames(nextProps.age); } } [/js] "Right, if there is the age passed, I load board games." If you don't know how this lifecycle works you may end up with the solution similar to above. But this lifecycle method doesn't work exactly like that. First, every time there is some change in component's props, componentWillReceiveProps is executed. It's quite easy to understand. But you may still think: "Okay, so every time the age is changed, it'll load board games. Isn't it okay?". Partially yes, but in most cases there are other props in your component. Props which will also trigger this lifecycle function. Imagine that we have also boardGames prop (where we store currently displayed board games). Let's examine such a situation:- Age prop is changed
- componentWillReceiveProps is executed (what causes board games' load)
- Board games prop is changed
- componentWillReceiveProps is executed (what causes board games' load)
- Board games prop is changed
- componentWillReceiveProps is executed (what causes board games' load)
- INFINITE LOOP!!!
Something else?
No, that's it. There are many different ways to crash or mess your React application but I chose these which I experienced myself. I hope it was a valuable reading for you. Now.. let's code!26 listopada 2019
0
Neo4j with Spring Boot
In this article, I will show you the advantages and disadvantages of the neo4j graph database, the technology that is being used by big companies like Google, Facebook or PayPal. I will also show you how to create and populate it with the help of Spring Boot. Why a graph database…? The main application of graph databases is to store relationship information as a first-class entity, because, despite the name, relational databases do not describe relationships other than the standard many-to-many, one-to-one and one-to-many. A huge advantage of graph databases is that the performance is not reduced with the growth of the amount of data. ...and why neo4j? Apart from the above points, the neo4j itself has a number of advantages, such as:
- scalability
- good documentation
- easy to use
- built-in Spring Boot support
- wide user community
10 czerwca 2019
0
6 Tips That Every MySQL User Should Know
Over the last 3 years, I have been working with MySQL almost every day. Even though non-relational databases like MongoDB are gaining more and more popularity every year, traditional SQL solutions are still widely used for many purposes. In this post, I will share some of my tricks I have been using to make my life easier. Note: most of those tips apply only to development machines, for production you should take more care.
1. Run MySQL in Docker
Seriously, in 2018 there is absolutely no need to run MySQL server natively by installing it, setting users and passwords, performing upgrades etc. You are wasting your client's time if you are still doing it. Just use the sample Docker Compose file as a working starting point: [code] version: '3' services: mysql: image: mysql:5.7 environment: - MYSQL_ROOT_PASSWORD=root - MYSQL_DATABASE=myproject ports: - 127.0.0.1:3306:3306 volumes: - ./varlibmysql:/var/lib/mysql [/code] After docker-compose up, you will have a working server with localhost-only port bound on standard port 3306, one user root/root and a pre-created "myproject" database. Throw in restart: always into Compose file if you want to keep the server running across reboots. That is enough for 95% of software projects, this solution is completely disposable and easy to recreate. Note: I still have MySQL client installed natively on my development machine. Technically speaking, there is a way of avoiding this too and running the client itself from Docker image, but that is a matter of preference.2. Store MySQL data in RAM
In Docker world, the best practice for handling data storage is to store it outside of the container filesystem - in case of a development machine, in some mounted directory on the host. A cool trick, at least on Linux, is to create a RAM-based filesystem called tmpfs and use it as data storage for MySQL. This will, of course, result in data loss after machine restart, but who cares about development data? If it is important, you should have a recent and verified backup anyway, right? In my case, I am mounting a folder /tmp/varlibmysql into container /var/lib/mysql, since I am using ramdisk for the whole temporary directory to limit SSD wearing. So the relevant part of Compose file is: [code] ... volumes: - /tmp/myproject/varlibmysql:/var/lib/mysql ... [/code] There is a noticeable performance gain with this configuration: I measured the time it takes to run a few hundred Liquibase migrations on application startup and the time it takes to import ~1GB database dump.- migrations: SSD 0:44, ramdisk 0:07 - huge speedup
- import: SSD 5:23, ramdisk 4:14 - small but noticeable speedup
3. Manage database dumps like a boss
I personally dislike edge case bugs, which often appear in web applications. One of the ways for a tester to describe such rarely occurring bug is to provide a list of bazillion intricate steps, that have to be carefully performed in order for the bug to appear. It is much easier to create a bug report with a database dump attached, which contains the whole state of the application. As a result, explaining and more importantly reproducing a bug is much easier and faster. However, if every time this happens there is a need to go to StackOverflow to recall mysqldump syntax, no one will want to do this. So let's fix the issue once and for all: [code] $ cat export.sh #! /bin/bash set -e DATABASE=myproject if [ "$#" -ne 1 ]; then echo "Usage: export.sh <filename.sql.bz2>" exit 1 fi echo "Exporting to $1..." mysqldump --protocol tcp -h localhost -u root -proot ${DATABASE} \ | pv \ | bzip2 > "$1" echo "Export finished." [/code] [code] $ cat import.sh #! /bin/bash set -e DATABASE=myproject if [ "$#" -ne 1 ]; then echo "Usage: import.sh <filename.sql.bz2>" exit 1 fi echo "Importing from $1..." bzcat "$1" \ | pv \ | mysql --protocol tcp -h localhost -u root -proot ${DATABASE} echo "Importing finished." [/code] [code] $ cat drop.sh #! /bin/bash set -e DATABASE=myproject echo "Dropping and recreating ${DATABASE} ..." mysql --protocol tcp -h localhost -u root -proot ${DATABASE} \ -e "drop database ${DATABASE}; create database ${DATABASE};" echo "Done." [/code] These are 3 scripts I use every day for SQL export, import and one extra for recreating a database for testing purposes. Those scripts use bzip2 compression for minimum file size and pv tool for visualising data flow.4. Log executed queries
Recently I have been fixing some performance problems in one of our projects. Our business contact reported that "this specific webpage is slow when there are lots of users present". I started looking around in Chrome Developer Tools and it became clear that the issue is on the backend side, as usual... I could not see any obvious bottlenecks in Java code, so I went a layer down into the database and yep, there was a performance problem there - some innocent SQL query was executed thousands of times for no reason. In order to debug such cases, query logging is a must, otherwise we are shooting in the dark. You can enable basic query logging using those commands in MySQL console: [code] MySQL [myproject]> SET global general_log = 1; Query OK, 0 rows affected (0.00 sec) MySQL [myproject]> SET global log_output = 'table'; Query OK, 0 rows affected (0.00 sec) [/code] From now on, all queries will be logged in special table mysql.general_log. Fun fact - this table is actually a real physical table, it can be searched, exported etc. - good for documenting bugfixes. Let's create some sample database structure: [code] MySQL [myproject]> create table random_numbers(number float); Query OK, 0 rows affected (0.00 sec) MySQL [myproject]> insert into random_numbers values (rand()), (rand()), (rand()), (rand()); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 [/code] And now run a few queries and see if they are captured in the log: [code] MySQL [myproject]> select * from random_numbers where number < 0.1; Empty set (0.00 sec) MySQL [myproject]> select * from random_numbers where number < 0.5; +----------+ | number | +----------+ | 0.254259 | +----------+ 1 row in set (0.00 sec) MySQL [myproject]> select * from random_numbers where number < 0.9; +----------+ | number | +----------+ | 0.777688 | | 0.254259 | +----------+ 2 rows in set (0.00 sec) MySQL [myproject]> select event_time, argument from mysql.general_log; +----------------------------+-------------------------------------------------+ | event_time | argument | +----------------------------+-------------------------------------------------+ | 2018-11-26 12:42:19.784295 | select * from random_numbers where number < 0.1 | | 2018-11-26 12:42:22.400308 | select * from random_numbers where number < 0.5 | | 2018-11-26 12:42:24.184330 | select * from random_numbers where number < 0.9 | | 2018-11-26 12:42:28.768540 | select * from mysql.general_log | +----------------------------+-------------------------------------------------+ 4 rows in set (0.00 sec) [/code] Perfect! Keep in mind that "all queries" means all of all, so if you are using graphical database tools for viewing query logs, those "query querying logs" will be also there.5. Use remote servers
MySQL protocol runs over TCP/IP (note to nitpickers: yes, it can also work through UNIX sockets, but the principle is the same). It is perfectly fine to use some remote MySQL server/service for local development instead of a local server. This is useful for working with big databases that would not fit onto tiny laptop SSD, or if we need more performance. The only concern is network latency, which can sometimes diminish any performance gains - but I think it is still a useful trick. Let's try Amazon RDS. It is pretty expensive for long-term usage but affordable for one-off tasks. Graphical setup wizard in AWS web console is pretty easy to use so I will not cover it here in full, however, keep attention on:- DB instance class - pick the cheapest one for start, you can always upgrade it later if needed
- Allocated storage - the minimum (20 GiB) is actually a huge amount of space for a typical project, but you can increase it now if you need
- Username and password - pick something secure, because our instance will be publicly visible from the Internet
- Security group - pick/create a security group with full access from the Internet
6. Learn some SQL!
Finally, even if you love ORMs and you can't live without Spring Data and sexy dynamic finders (which can sometimes be long enough to wrap on 4k screen), it is very beneficial to learn at least some SQL to understand how everything works underneath, how ORMs are mapping one-to-many and many-to-many relationships with the use of extra join tables, how transactions work (very important in high load systems) and so on. Also, some kinds of performance problems ("N+1 select" being the most common) are completely undiscoverable without knowing how underlying SQL is generated and subsequently executed. And that is all. Thanks for reaching this point and I hope you've learnt something.15 kwietnia 2019
0
Let’s shake some trees – how to enhance the performance of your application
Nowadays JavaScript applications are getting bigger and bigger. One of the most crucial things while developing is to optimise the page load time by reducing the size of the JavaScript bundle file. JavaScript is an expensive resource when processing and should be compressed when it is about to be sent over the network. One of the most popular techniques to improve the performance of our applications is code splitting. It is based on splitting the application into chunks and serving only those parts of JavaScript code that are needed at the specified time. However, this article is going to be about another good practice called tree shaking. Tree shaking is used within the ES2015 import and export syntax and supports the dead-code elimination. Since Webpack 4 released it is possible to provide the hint for a compiler by the “sideEffects” property to point the modules that can be safely pruned from the application tree if unused. Function may be supposed to have side effects if it modifies something outside its own scope. Real life example In order to introduce tree shaking concept more precisely, let’s start with creating a new project including the application entry point (index.js) and the output bundle file (main.js). In the next step, a new JavaScript file (utils.js) is added to the src directory... [code] export function foo() { console.log('First testing function') } export function bar() { console.log('Second testing function') } [/code] ...and imported in the index.js. [code] import { foo } from './utils.js' foo() [/code] Webpack 4 introduces the production and development mode. In order to get a not minified output bundle, we are supposed to run a build process in the development mode what can be defined in the package.json file. [code] "scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" } [/code] Now, just get the terminal and run: npm run build script. Despite, only the foo function has been required in the entry point, our output bundle still consists of both foo and bar methods. Bar function is known as a “dead code” since it is unused export that should be dropped. [code] console.log('First testing function');\n\nfunction bar() {\n console.log('Second testing function') [/code] To fix this problem we are about to set the “sideEffects” property in package.json file. [code] { "name": "tree-shaking", "version": "1.0.0", "sideEffects": "false", } [/code] That property just tells the compiler that there are not any side effects files and every unused code can be pruned. It accepts also absolute and relative paths to the files that should not be dropped due having some side effects. [code] { "name": "tree-shaking", "version": "1.0.0", "sideEffects": "./src/file-wth-side-effects.js", } [/code] Minification After we pruned unused ES6 imports and exports, we still need to remove “dead code” from the application bundle. The only thing we have to do is to set the mode configuration to production and execute npm run build. [code] ([function(e,t,n){"use strict";n.r(t),console.log("First testing function")}]); [/code] As we can see, the second testing function is no more included in the bundle minified file. Use three shaking with the ES6 It is crucial to keep in mind that tree shaking pattern can be used only within the ES6 import and export modules. We cannot “shake the tree” while using the CommonJS without the help of special plugins. To solve this issue, setting the babel-preset-env to leave the ES6 modules on their own should be performed. [code] { "presets": [ ["env", { "modules": false }] ] } [/code] Exception Removing unused modules does not work while dealing with lodash. Lodash is one of the most popular utility library. If you import a module in the way it is depicted below, it will still pull all the lodash library. [code] import { join } from 'lodash' [/code] To go around that, we need to install lodash-es package and require the modules in the following way: [code] import join from 'lodash-es/join' [/code] Conclusion Let’s prove the statement from the beginning of this article! Let’s take a closer look at the sizes of the bundle file (main.js) before and after the tree shaking and minification process. As we can see we minimized the output size significantly. When you start using tree shaking in your projects it may seem not taking a big advantage of application performance at all. You will notice how it really boosts up your work while having a more complex application tree.
14 stycznia 2019
0
Creating Countinous Integration system with GitLab and Unity
After spending days and nights working in the sweat of our brows and making tons of application builds, we finally decided to take a step forward and make some part of our job automatic. But... why ? The main reason of making our Continuous Integration system was shortening amount of time spent on building applications. In our last project it lasted even above half an hour, so we decided to make our life easier. We had a list of features we wanted to achieve:
- builds available online to download
- working on multiple unity versions
- building for iOS, Windows and Android on our Windows device
- builds available on our local server
C:\’Program Files’\Unity\Editor\Unity.exe | call unity executable |
-batchmode | execute in batchmode to prevent the editor from opening |
-nographics | needed on windows to really make sure there is no GUI |
-executeMethod BuildScript.Build | calls the Method “Build()” in the Unity BuildScript |
-projectPath %CI_PROJECT_DIR% | sets the unity project path to default GitLab directory |
-quit | quit Unity after execution of the build |
21 września 2018
0
Kubernetes for poor – how to run your cluster and not go bankrupt
Since the last few years, Kubernetes has proved that it is the best container orchestration software on the market. Today, all 3 biggest cloud providers (Amazon, Google and Azure) are offering a form of managed Kubernetes cluster in form of EKS, GKE and AKS respectively. All of those offerings are production-ready, they are fully integrated with other cloud services and they include commercial support. There is one problem, though, and it is the price. Typically, cloud offerings like this are targeted for big organizations with a lot of money. For example, Amazon Elastic Kubernetes Service costs 144$/mo for just running cluster management ("control plane"), and all compute nodes and storage are billed separately. Google Kubernetes Engine does not charge anything for the control plane, but instances to run nodes at aren't cheap either - a reasonable 4 GiB machine with only a single core costs 24$/mo. The question is, can we do it cheaper? Since Kubernetes itself is 100% open source, we can install it on our own server of choice. How much we'll be able to save? Big traditional VPS providers (like OVH or Linode for instance) give out decent machines at prices ranging from 5$/mo. Could this work? Well, let's try it out!
Setting up the server
Hardware
For running our single-master single-node host we don't need anything too fancy. The official requirements for a cluster bootstrapped by kubeadm tool, which we are going to use, are as follows:- 2 GiB of RAM
- 2 CPU cores
- reasonable disk size for basic host software, Kubernetes native binaries and it's Docker images
Installing Docker and kubeadm
Here we will be basically following the official guide for kubeadm, which is a tool for bootstrapping a cluster on any supported Linux machine. First thing is Docker - we'll install it through the default Ubuntu repo, but generally, we should pay close attention to Docker version - only specific ones are supported. I've run into many problems creating a cluster with the current newest version (18.06-*), so I think this is worth mentioning. [code] $ apt-get update $ apt-get install -y docker.io [/code] And let's run a sample image to check if the installation was successful: [code] $ docker run --rm -ti hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 9db2ca6ccae0: Pull complete Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa32... Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. (...) $ [/code] Good. The next step is kubeadm itself - note that the last command (apt-mark hold) will prevent APT from automatically upgrading those packages if a new version appears. This is critical because cluster upgrades are not possible to be done automatically in a self-managed environment. [code] $ apt-get update && apt-get install -y apt-transport-https curl $ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg \ | apt-key add - $ cat <<EOF >/etc/apt/sources.list.d/kubernetes.list deb http://apt.kubernetes.io/ kubernetes-xenial main EOF $ apt-get update $ apt-get install -y kubelet kubeadm kubectl $ apt-mark hold kubelet kubeadm kubectl [/code]Setting up a master node
The next step is to actually deploy Kubernetes into our server. Those 2 basic commands below initialize the master node and select Flannel as an inter-container network provider (an out-of-the-box zero-config plugin). People with experience with Docker Swarm will immediately notice the similarity in init command below - here we specify two IP addresses:- apiserver advertise address is the address at which, well, apiserver will be listening - for single-node clusters we could put 127.0.0.1 there, but specifying external IP of the server here will allow us to add more nodes to the cluster later, if necessary,
- pod network CIDR is a range of IP addresses for pods, this is dictated by the network plugin that we are going to use - for Flannel it must be that way, period.
Testing our setup
Testing - pod scheduler
Kubernetes is a really complex piece of container orchestration software, and especially in the context of a self-hosted cluster, we need to make sure that everything has been set up correctly. Let's perform one of the simplest tests, which is to run the same hello-world image, but this time, instead of running it directly through Docker API, we'll tell Kubernetes API to run it for us.
[code]
$ kubectl run --rm --restart=Never -ti --image=hello-world my-test-pod
(...)
Hello from Docker!
This message shows that your installation appears to be working correctly.
(...)
pod "my-test-pod" deleted
[/code]
That's a bit long command. Let's explain in detail what are the parameters and what just happened.
When we are using kubectl command, we are talking through the apiserver. This component is responsible for taking our requests (under the hood, HTTP REST requests, but we are using it locally) and communicating our needs to other components. Here we are asking to run an image of name hello-world inside temporary pod named my-test-pod (just a single container), without any restart policy and with automatic removal after exit. After running this command, Kubernetes will find a suitable node for our workload (here we have just one, of course), pull the image, run its entrypoint command and serve us it's console output. After the process finishes, pod is deleted and the command exits.
Testing - networking
The next test is to check if networking is set up correctly. For this, we will run Apache web server instance - conveniently, it has a default index.html page, which we can use for our test. I'm not going to cover everything in YAML configurations (that would take forever) - please check out pretty decent official documentation. I'm just going to briefly go through concepts. Kubernetes configuration mostly consists of so-called "resources", and here we define two of them, one Deployment (which is responsible for keeping our Apache server always running) and one Service of type NodePort (which will allow us to connect to Apache under assigned random port on the host node).
[code]
$ cat apache-nodeport.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apache-nodeport-test
spec:
selector:
matchLabels:
app: apache-nodeport-test
replicas: 1
template:
metadata:
labels:
app: apache-nodeport-test
spec:
containers:
- name: apache
image: httpd
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: apache-nodeport-test
spec:
selector:
app: apache-nodeport-test
type: NodePort
ports:
- port: 80
$ kubectl apply -f apache-nodeport.yml
[/code]
It should be up and running in a while:
[code]
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
apache-nodeport-test-79c84b9fbb-flc9p 1/1 Running 0 25s
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
apache-nodeport-test NodePort 10.108.114.13 <none> 80:31456/TCP
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP
[/code]
Let's try curl-ing our server, using a port that was assigned to our service above:
[code]
$ curl http://139.59.211.151:31456
<html><body>
<h1>It works!</h1>
</body></html>
[/code]
And we are all set!
How to survive in the Kubernetes world outside a managed cloud
Most DevOps people, when they think "Kubernetes" they are thinking about a managed offering of one of the biggest cloud providers, namely Amazon, Google Cloud Platform or Azure. And it makes sense - such provider-hosted environments are really easy to work with, they provide cloud-expected features like metrics based autoscaling (both on container count and node count level), load balancing or self-healing. Furthermore, they provide well-integrated solutions to (in my opinion) two biggest challenges of containerized environments - networking and storage. Let's tackle the networking problem first.
Kubernetes networking outside the cloud
How do we access our application running on the server, from the outside Internet? Let's assume we want to run a Spring Boot web hello world. In a typical traditional deployment, we'll have our Java process running on the host, bound to port 8080 listening to traffic, and that's it. In case of containers and especially Kubernetes, things get complicated really quickly.
Every running container is living inside pod, which has its own IP address from pod network CIDR range. This IP is completely private and invisible for clients trying to access the pod from the outside world.
The managed cloud solution, in case of Amazon for example, is to create a Service with type LoadBalancer, which will end up creating an ELB instance in your customer account (a whooping 20$/mo minimum), pointing to the appropriate pod. This is, of course, impossible to do in a self-hosted environment, so what can we do? Well, a hacky solution is to use NodePort services everywhere, which will expose our services at high-numbered ports. This is problematic because of those high port numbers, so we slap an Nginx proxy before them, with appropriate virtual hosts and call it a day. This will definitely work, but for such use case Kubernetes provides us something called Ingress Controller, that can be deployed into a self-hosted cluster. Without going into much detail, it's like installing Nginx inside Kubernetes cluster itself, which makes it possible to control domains and virtual hosts easily through the same resources and API.
Deploying official Nginx ingress is just a few kubectl apply commands away as usual, however, there is a catch - official YAML-s actually are using a NodePort service for Nginx itself! So that won't really work, because all our applications would be visible on this high port number. The fix to this is to do what Kubernetes explicitly discourages, which is to use fixed hostPort binding for 80 and 443 ports. This way, we'll be able to access our apps from the browser without any complications. Ingress definitions with those changes can be found at my GitHub, but I encourage you to see the official examples and start from there.
[code]
$ kubectl apply -f .
$ (...)
$ curl localhost
default backend - 404
$
[/code]
If you have a domain name for your server already (or you made a static entry in /etc/hosts), you should be able to point your browser at the domain and see "404 - default backend". That means our ingress is running correctly, serving 404 as a default response, since we haven't defined any Ingress resource yet. Let's use our Nginx Ingress to deploy the same Apache server, using domain names instead of random ports. I set up a static DNS entry in hosts file with name kubernetes-for-poor.test here.
[code]
$ cat apache-ingress.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-test
spec:
selector:
matchLabels:
app: ingress-test
replicas: 1
template:
metadata:
labels:
app: ingress-test
spec:
containers:
- name: ingress-test
image: httpd
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ingress-test
spec:
selector:
app: ingress-test
ports:
- port: 80
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-test
spec:
rules:
- host: kubernetes-for-poor.test
http:
paths:
- path: '/'
backend:
serviceName: ingress-test
servicePort: 80
$ kubectl apply -f apache-ingress.yml
deployment.apps/ingress-test created
service/ingress-test created
ingress.extensions/ingress-test created
$
[/code]
And pointing your browser at your domain should yield "It works!" page. Well done! Now you can deploy as many services as you like. An easy trick is to create DNS CNAME record, pointing from *.yourdomain.com to yourdomain.com - this way there is no need to create additional DNS entries for new applications.
Kubernetes storage outside the cloud
The second biggest challenge in the container world is storage. Containers are by definition ephemeral - when a process is running in a container, it can make changes to the filesystem inside it, but every container recreation will result in loss of this data. Docker solves it by the concept of volumes, which is basically mounting a directory from the host at some mount point in container filesystem, so changes are preserved.
Unfortunately, this by definition works only for single-node deployments. Cloud offerings like GKE solve this problem by allowing to mount a networked storage volume (like GCE Persistent Disk) to the container. Since all nodes in the cluster can access PD volumes, storage problem goes away. If we add automatic volume provisioning and lifecycle management provided by PersistentVolumeClaim mechanism (used to request storage from the cloud in an automated manner), we are all set.
But none of the above is possible on a single-node cluster (outside of managing a networked filesystem like NFS or Gluster manually, but that's not fun). The first thing that comes to mind is to go the Docker way and just mount directories from the host. Since we have only 1 node, it shouldn't be a problem, right? Technically true, but then we can't use PersistentVolumeClaims and we have to keep track of those directories, make sure they exist beforehand etc. There is a better way, though. We can use so-called "hostpath provisioner", which uses Docker-like directory mounts under the hood, but exposes everything through PVC mechanism. This way volumes are created when needed and deleted when not used anymore. One of the implementations which I've tested can be found here, it should work out of the box after just another kubectl apply.
Let's test it. For this example, we'll create a single, typical PVC.
[code]
$ kubectl apply -f .
$ (...)
$ cat test-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Mi
$ kubectl apply -f pvc.yml
$ kubectl get pvc
test-pvc Bound pvc-a5109ca0... 200Mi RWO hostpath 5s
[/code]
We can see that our claim was fulfilled and the new volume was bound to the claim. Let's write some data to the volume and check if it is present on the host.
[code]
$ cat test-pvc-pod.yml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp-container
image: ubuntu
command: ['bash', '-c', 'while true; do sleep 1 && echo IT_WORKS | tee -a /my-volume/test.txt; done']
volumeMounts:
- mountPath: /my-volume
name: test
volumes:
- name: test
persistentVolumeClaim:
claimName: test-pvc
$ kubectl apply -f test-pvc-pod.yml
(...)
$ ls /var/kubernetes
default-test-pvc-pvc-a5109ca0-b289-11e8-bc89-fa163e350fbc
$ cat default-test-pvc-pvc-a5109ca0-b289-11e8-bc89-fa163e350fbc/test.txt
IT_WORKS
IT_WORKS
(...)
[/code]
Great - we have our data persisted outside. If you can keep all your applications running on the cluster, then backups become a piece of cake - all the state is kept in a single folder.
Let's actually deploy something
We have our single-node cluster up and running, ready to run any Docker image, with external HTTP connectivity and automatic storage handling. As an example, let's deploy something everyone is familiar with, namely Wordpress. We'll use official Google tutorial with just one small change: we want our blog to be visible under our domain name, so we remove "LoadBalancer" from service definition and add an appropriate Ingress definition.
[code]
$ cat wp.yml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
spec:
selector:
matchLabels:
app: wordpress-mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress-mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
spec:
ports:
- port: 3306
selector:
app: wordpress-mysql
---
apiVersion: v1
kind: Service
metadata:
name: wordpress-web
labels:
app: wordpress-web
spec:
ports:
- port: 80
selector:
app: wordpress-web
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: wordpress-web
spec:
selector:
matchLabels:
app: wordpress-web
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress-web
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: wordpress-web
spec:
rules:
- host: kubernetes-for-poor.test
http:
paths:
- path: /
backend:
serviceName: wordpress-web
servicePort: 80
$
[/code]
The only prerequisite is to create a Secret with the password for MySQL root user. We could easily and safely hardcode 'root/root' in this case (since our database isn't exposed outside the cluster), but we'll do it just to follow the tutorial.
[code]
$ kubectl create secret generic mysql-pass --from-literal=password=sosecret
[/code]
And we finally deploy.
[code]
$ kubectl apply -f wp.yml
[/code]
After a while, you should see a very familiar Wordpress installation screen at the domain specified in the Ingress definition. Database configuration is handled by dockerized Wordpress startup scripts, so there is no need to do it here like in a traditional install.
Conclusion
And that's it! Fully functional single-node Kubernetes cluster, for all our personal projects. Is this an overkill for a few non-critical websites? Probably yes, but it's the learning process that's important. How would you become a 15k programmer otherwise?
19 września 2018
1
I’ve got the power – how to control remotely your PC using Grails
In this article I will show you how to control remotely your windows computer via the local network using Linux machine and Grails application. First we will turn on a device with Wake-On-LAN (WOL) method and then turn it off using RPC shutdown command call. After this few steps, you won't have to push the power button on your computer any more. 1. Turn on a computer - wake up, darling!!! To begin with we have to check a few configuration things:
- check if BIOS allows to use Wake-On-LAN on the computer. Go to BIOS power management settings and find the Wake-On-Lan configuration there. If related option does not exist it probably means that BIOS automatically supports WOL - most of new hardware does that. When you find an appropriate option check if it is enabled. Depends on your computer motherboard, it is automatically enabled or you will have to set it manually.
- check your system configuration. Open Windows Device Manager, find your local network device. Right click on a device and select "Properties". Next, select Advanced tab, find a "Wake on Magic Packet" option and check if it is enabled.
- Firstly, check if your Linux system has samba packages installed. We will use "net rpc" command to communicate with remote computer. For Ubuntu/Debian Linux distribution it is included in samba-common-bin package. To install samba use console command "sudo apt-get install samba-common-bin".
- Configure your windows machine to disable UAC remote restrictions. Locate the following registry subkey: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System. Check if the LocalAccountTokenFilterPolicy registry entry does exist. If not, create a new entry with DWORD value "1".
- check if firewall has opened port 445 for TCP connection. When not exist then you should add a new role in the firewall.
21 sierpnia 2018
1
Populate database in Spring
Once upon a time there was BootStrap class. The class was a very friendly class, therefore it befriended many more classes. With more classes inside, BootStrap grew bigger and bigger, expanding itself at the rate of the entire universe. This is not a fairy tale.
This is not a fairy tale, because this is exactly what happened. But first things first, you may wonder what the BootStrap
is. It's a mechanism coming from Grails Framework, which executes code on application startup; I'm using it mostly to populate database. Just put the BootStrap.groovy
in grails-app/init
folder and add some goodies to the init
closure. Having a background in Grails, this is something I missed in Spring, especially because as I mentioned before, the code grew fairly big. I wanted to rewrite the whole BootStrap logic in Java, because its older Groovy version somehow reminded me of poorly written tests you may see here and there: verbose and ugly. It just wasn't a first-class citizen of the production code.
[java]
@Log4j @AllArgsConstructor
@Component
public class BootStrap {
private final BootStrapService bootStrapService;
@EventListener(ApplicationReadyEvent.class)
private void init() {
try {
log.info("BootStrap start");
bootStrapService.boot();
log.info("BootStrap success");
} catch (Exception e) {
log.error("BootStrap failed," + e);
e.printStackTrace();
throw e;
}
}
}
[/java]
Surprise, surprise! There's an EventListener
annotation that you can put on a method in order to track the ApplicationReadyEvent
and run some code on application startup. Job's done, right? Well, not really, you CAN do that, but do you WANT TO do that? I prefer to keep the business logic in a service, therefore I created and injected BootStrapService
, that just leaves logging and error handling here and Lombok's annotations make the whole thing even neater.
[java]
public abstract class BootStrapService {
@Autowired
protected BootStrapEntryService entryService;
@Autowired
protected MovieService movieService;
@Transactional
public void boot() {
writeDefaults();
}
protected void writeDefaults() {
entryService.createIfNotExists(BootStrapLabel.CREATE_MOVIE, this::createMovie);
}
private void createMovie() {
movieService.create("Movie");
}
}
[/java]
I made the boot
method @Transactional
, because it's our starting point to populate database, later in the project you might want to add here some data migration as well. The REAL rocket science begins in writeDefaults
method! The example createMovie
method reference is passed as a parameter, with double colon as a syntactic sugar, to the createIfNotExists
method of the injected BootStrapEntryService. The other parameter is a simple BootStrapLabel
enum, with a value used as a description for a given operation. I prefer to add a verb as a prefix, just not to be confused later, when a possibility of other operations comes up.
[java]
@Log4j @AllArgsConstructor
@Transactional
@Service
public class BootStrapEntryService {
private final BootStrapEntryRepository bootStrapEntryRepository;
public void createIfNotExists(BootStrapLabel label, Runnable runnable) {
String entryStatus = "already in db";
boolean entryExists = existsByLabel(label);
if(!entryExists) {
runnable.run();
create(label);
entryStatus = "creating";
}
log(label, entryStatus);
}
public boolean existsByLabel(BootStrapLabel label) {
return bootStrapEntryRepository.existsByLabel(label);
}
public BootStrapEntry create(BootStrapLabel label) {
BootStrapEntry bootStrapEntry = new BootStrapEntry();
bootStrapEntry.setLabel(label);
return bootStrapEntryRepository.save(bootStrapEntry);
}
private void log(BootStrapLabel label, String entryStatus) {
String entryMessage = "processing " + label + " -> " + entryStatus;
log.info(entryMessage);
}
}
[/java]
Finally, createIfNotExists
method is the place to call the actual methods to populate database, however, in a generic way. Method passed as a reference may be called, however, we don't want to write the data that was written to the database before, at least considering a not in-memory database, so we check if an entry for a given label already exists. We have to create an entity, a pretty simple entity, in this case the BootStrapEntry
, with just a label field to keep the labels in database. existsByLabel
and create
are simple generic methods responsible for basic database operations on the labels.
[java]
@Profile("development")
@Service
public class DevelopmentBootStrapService extends BootStrapService {
@Autowired
private BookService bookService;
@Override
protected void writeDefaults() {
super.writeDefaults();
entryService.createIfNotExists(BootStrapLabel.CREATE_BOOK, this::createBook);
}
private void createBook() {
bookService.create("Book");
}
}
[/java]
Now we're getting somewhere! If you wondered why I made the BootStrapService
abstract
, now is the answer. I wanted to make it possible to run some code only in a given environment, like development
. The Profile
annotation with environment name as a parameter comes in hand. Overriding the writeDefaults
method provides a way to initialize some data only in development
.
[java]
@Profile("production")
@Service
public class ProductionBootStrapService extends BootStrapService {
}
[/java]
If there is the development
, there may be the production
as well. In the given example, I just wanted to run the default data from the parent class, without filling the environment with some random data.
That's all, my little guide to populate database in Spring. Hopefully, it wasn't THAT bad and even if it was, feel free to leave the feedback nonetheless!
14 maja 2018
0
How GitLab helps us move fast at itSilesia
Continuous Integration and Continuous Delivery have taken the world by storm. The nature of our business forces development teams to move quickly and be as efficient as possible, both in regards to standard software development, but also it’s delivery and quality assurance. In order to achieve these goals, we need tools that will enable us to reach them in a simple way. Thankfully, today we have access to a lot of CI/CD solutions, both free and paid, all very different, with different features and different goals in mind. In essence they all do the same thing - allow us to work smarter, not harder. Our previous development infrastructure consisted of three parts. For issue tracking and Scrum process we used Redmine with Scrum Board plugin. It worked fine and served us great for years, but from today’s perspective it's UI is seriously outdated and generally hard to use. We probably could give it a try to upgrade it, but sadly it is so old that we really do not want to try it. For hosting Git repositories we used Gitolite Redmine integration. It also worked fine, but it's functionality is nonexistent compared to modern solutions, it lacks really basic functionalities like Pull Requests or commenting on commits. For continuous integration we were (and somewhat still are) using Jenkins. Now, don't get me wrong - Jenkins is great and very powerful, requires minimal setup, can be scaled and we achieved some great success with it, especially with use of Groovy Pipelines. But here is the question, if there is a tool (GitLab, if you didn't read the title) that could integrate all of the above, even for some loss of customizability and potential vendor lock-in, is it worth it? Well, let's check it out! 1. Setup How do we start then? The first decision to make is whether we want to use GitLab.com hosted service, or create our private GitLab instance on company infrastructure. This decision was simple to make - we don't want to store the most valuable company asset (the source code) on public infrastructure, and given the recent problems with GitLab accessibility - this is just a better option. There are multiple ways of installing GitLab, but the easiest way for us was using prebuilt "batteries included" Docker image, which contains all services in one big bundle. This way of packaging multiple applications into one monolithic Docker image is a bit controversial in the community (the best practice is to join individual processes running in separate containers using Docker Compose, for example), but in my opinion it works great for such big and complex software packages, because all the internal complexity is completely hidden away. As far as installation is concerned, we already have a few Docker hosts, so running an additional container somewhere isn’t a big deal. Documentation specifies that the following Docker invocation is a good start: [code] sudo docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 --publish 80:80 --publish 22:22 \ --name gitlab \ --restart always \ --volume /srv/gitlab/config:/etc/gitlab \ --volume /srv/gitlab/logs:/var/log/gitlab \ --volume /srv/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest [/code] As with everything shipped in a form of a Docker image, we could just blindly copy and paste it into a privileged console... but let's slow down. Do we really understand what these parameters mean? First of all, on most Linux systems port 22 is already used by SSH server, so another binding on port 22 will surely conflict. SSH port is used by GitLab to run it's internal SSH Git server. Since we are perfectly happy with using only HTTPS as before, we can remove this binding altogether. Also, in our case (and in most cases on typical production systems) HTTP(S) ports (80 and 443) are already taken by some Apache or Nginx web server running natively. Since we wanted to use our external Apache proxy (which was also doing SSL termination) on the company edge router, we had to change HTTP port binding to some other random-ish value. Also, we can remove the hostname, it seems not to affect anything. After a few minutes of some heavy internal provisioning and initialization, you can visit the main page. It will ask for an administrator password first, then it will allow to log in. I have to admit that I was very pleasantly surprised by how easy this setup process was. I will not get deep into typical administrative stuff. As a first thing you probably want to create users, assign them to groups, create repositories and hand out permissions. It's not really interesting to be honest. 2. Projects The next topic is migrating projects. Since Git is a decentralized system, it doesn't really matter how many repository servers are used for a single project. However, even though we could work this way (and technically we are), we generally don't think in a decentralized way - typically there is just one central repository. This becomes a challenge, when you want to migrate existing projects when developers are working on them at the same time. The first step is fairly easy - open a console (you are using Git from console, right?), create a new Git remote, and then push all branches to newly created GitLab repository. In the following examples I'll use a very simple Spring Boot application (you can find it here). [code] cd ~/luckynumber git remote add gitlab \ https://gitlab.gliwice.itsilesia.com/agrzybowski/luckynumber.git git push -u gitlab --all git push -u gitlab --tags [/code] After some uploading, you will end up with two identical repositories. And... The issue is that now we have 2 remotes, which means that if you previously had branch develop (and it's remote tracking branch counterpart origin/develop), now there is a new tracking branch gitlab/develop. And that's only on your local repository - other team members can't know (by definition of decentralized model) that there is another remote somewhere. There are two ways of dealing with this. The easiest way is to go around the office and yell "Guys, please copy your files, delete the project and reclone from scratch". And this might work, but expect a lot of hasty copy-pasting and useless bulk commits after this procedure. There is of course a better way. Git has an option to simply change the remote URL. Send your coworkers a Slack message to run this: [code] cd ~/luckynumber git remote set-url \ https://gitlab.gliwice.itsilesia.com/agrzybowski/luckynumber.git [/code] Be careful, though - all remote branch references (origin/xxx) will immediately get out of sync, so push their local counterparts as soon as possible. It’s a good idea to treat remote branches as volatile and not get too emotionally attached to them, because they can be overwritten at any time (remember, git rebase is your best friend, if used correctly). If they are important, don’t use them - just creating a local branch pointing to some commit will make it persistent forever. 3. The interesting part Now let's focus on CI/CD part. Glancing over the docs, the first thing that pops up is that GitLab CI is very tightly integrated to the source code repository. All configuration (in form of YAML file) is stored directly in the repository, jobs are based on branches and triggered on pushes, pull requests have little red/green checkmarks with build statuses and so on. Is this a good idea altogether? In my opinion it is, but only for the CI part, like running tests or collecting code quality metrics. Deployment (especially on production environments) should be decoupled from any version control system the application happens to use. But since we can get away with this in our internal projects (we have relatively little risk and hopefully responsible developers), we decided to go 100% GitLab and do our production deployments also there. GitLab CI configuration is stored in .gitlab-ci.yml file. It has a specific structure, which we will not cover in full here. The documentation is very comprehensive, and you can also find some examples online. We will be building an almost barebones Spring Boot application with a few unit tests. [code] image: openjdk:8-jdk stages: - test - package - deploy test: stage: test script: - ./gradlew test package: stage: package only: - master - develop script: - ./gradlew bootRepackage artifacts: paths: - build/libs/luckynumber-0.0.1-SNAPSHOT.jar deploy-test: stage: deploy only: - develop script: - ./deploy-test.sh deploy-prod: stage: deploy only: - master script: - ./deploy-prod.sh [/code] There is a lot of stuff going on here, let's break it down. First of all, the recommended way of building/testing/packaging your application is by using ephemeral Docker containers for creating temporary and 100% reproducible build environments. We went through a lot of pain on traditional Jenkins because of conflicting JVM versions, badly installed Grails distributions or even globally installed NPM packages (which "no one remembers installing"...). Using Docker containers removes this problem completely - every time a build is triggered there is a guaranteed fresh environment available. Here we specify OpenJDK public image, but you can use any, even your own. After setting up the image name, we define build stages. We use very typical steps - build/package/deploy, but they are of course arbitrary. For each stage we can define:
- branch constraints - for example, deploy to production only from master,
- artifacts - they are preserved across stages and stored internally in GitLab for later download,
- a Bash script - defining what to actually do.
2 marca 2018
1
Junior Unity Developer poszukiwany
Oferujemy: 1. pracę nad ciekawymi projektami 2. wsparcie doświadczonych Developerów 3. elastyczny grafik 4. dostęp do nowoczesnego sprzętu multimedialnego Wymagania: 1. podstawowa znajomość środowiska Unity 3D 2. ukończone cztery semestry na Politechnice Śląskiej (lub równorzędnej z innej części kraju) 3. podstawowa znajomość programowania w wysokopoziomowych językach obiektowych 4. umiejętność czytania kodu 5. umiejętność rozwiązywania problemów Mile widziane: 1. projekty wykonane w Unity 3D 2. odbyte szkolenia i wykonane samouczki dotyczące Unity 3D 3. zainteresowanie tematyką gier komputerowych i mobilnych 4. znajomość języka angielskiego i/lub niemieckiego na poziomie komunikatywnym 5. znajomość narzędzi pracy zespołowej i kontroli wersji (redmine) Jesteś zainteresowany, skontaktuj się z nami praca@itsilesia.com
15 lutego 2018
0
Star Wars opening crawl based on CSS animations and transformations
10 years ago nobody expected how frontend is going to evolve in the future. Developers from around the world were using CSS for basic styling because then only that was possible with CSS. Complex things, like animations or transformations, were made thanks to plain Javascript or, very popular at this time, jQuery framework. Currently, with CSS 3, HTML 5 and tons of Javascript frameworks for every possible use, we are in a totally different programming world. CSS has immense ecosystem of styles, which help us create, color, filter, transform or even animate objects on a screen. In this article I want to show a simple way to create animated Star Wars opening crawl using only HTML and CSS. There are a couple of methods to achieve this goal but we will try to choose the one that has the best performance. You may ask: "Why Star Wars opening crawl? There are so many interesting topics to choose from." Yeah, it's true but this task is relatively easy to implement and, what is more important, we need the most important CSS styles for animations and transformations to achieve it. And I love Star Wars. 1. Setup At first, we have to prepare HTML and basic CSS for displaying opening crawl. The only thing we need in HTML body is this simple piece of code: [html] <p>Here put your opening crawl story</p> [/html] Now we need to apply basic styles to the background of our scene and to the crawl's content. Original Star Wars crawl contains blackish background and yellow text sliding on a screen. This can be done simply by adding this CSS code: [css] body { background: #111; } p { margin: 0 auto; color: #fcd000; font-size: 30px; font-weight: bold; width: 600px; } [/css] Nothing special - just yellow text on a black background, you probably did it in your primary school. Fortunately, now we are going to use all the magic of CSS to make it ride. 2. Transformations As you can see on the featured photo, our text must be leaned. To make it work we need one simple but very powerful style - transform. It prpvides us a lot of transformation functions but the most important are these, which are connected with translation, rotation and scaling. Currently, on most browsers we can use it in 3 dimensions (x, y, z). In our case try to imagine that you put our <p> text into XYZ plot. We want to lean this text forward, in mathematical words, rotate around X axis. And this is also pretty easy to do with CSS - just add this chunk of code to <p>: [css]transform: rotateX(30deg)[/css] If you try to write the code along with me, you can see that this is certainly not what we expected. It looks more like shrinking than rotating with perspective. Why? The keyword here is the perspective. We didn't declare this perspective, so browser did it for us and set perspective to none. As a result, there isn't any visible perspective. How can we declare a perspective? There are two methods which are a little bit different. We can attach perspective to the transformed object or a parent of this object. In that case there is no difference but it's extremely important to use the second approach if you want to transform more than one object. If you attach perspective to the parent, all children will belong to one perspective. And this is how it works in real life! On the other hand, you may find situations where "perspective per object" approach is better - mostly when you don't need realistic behaviour but only nice looking effect. In our task we will use parent's perspective. To do this we have to put the code below into our styles (in our case these are <body> styles). For the sake of testing try this value: [css]perspective: 50px[/css] I must be honest, for a long time (waaaaay too long) I was writing random pixels' value into this property and checking if it's correct. But inside, I've always wanted to understand what these pixels mean. Nowadays, my beard is thicker, I switched from tea to coffee and finally I understand perspective property, yay! MDN documentation explains, that this value is a distance between user's eyes and z = 0 value on invisible plot. I will try to explain it more clearly. If you make perspective value very small, it looks like you are standing very close to the origin of object. Vice versa - if you provide really big value, you see an object from a really far distance. With this knowledge you can estimate what value you should type here. In our case our block of text is 600 pixels wide. We want to get the effect of seeing the crawl very close, so we should consider value, which is much lower than this 600 pixels. In our test we used 50 pixels but we can see that we "are standing" almost inside the block of text, so we should increase it to at least 150 pixels. That should create a pretty nice effect of static Star Wars opening crawl. [css] body { background: #111; perspective: 200px; } p { font-size: 30px; color: #fcd000; font-weight: bold; width: 600px; margin: 0 auto; transform: rotateX(30deg); } [/css] 3. Animation We've got the text on a screen, it's leaned as we wanted but we still need to make it move. At this moment we have to decide which way we choose - and this will result in how efficient our code will be. Practically, we have three significant methods to do this - 1. margin-top property, 2. position absolute/fixed with top property or 3. transform property using translate relative to Y axis. Modifying margin is the worst choice. Seriously, never ever do that! Margin property was never created by browsers developers to use it in complex animations. Okay, in this simple example of one animated block of text, it's possible that you won't see any difference. But try to animate hundreds of objects on a screen with margin and you will see that you should forget about this method. And if you need just a simple animation it's still not a good choice. Why would you use something worse if you have something better? Speaking about something better.. ..top property. Certainly, it is a better option, but still not the best. In the current frontend world there are many popular applications which use top/left properties to animate movement on scrollbars, sliders or animated dropdowns. But these examples are relatively simple tasks for our powerful devices (don't tell me your 2014 smartphone is not powerful, it is powerful enough). It has one advantage over next method - it's more compatible so if you need an effect which works on older browsers maybe it's better. For complex visualizations or simple games using many animations you should always choose transform over margin or positioning. Browsers have different approaches to render DOM but mostly they use a different way to render elements transformed with transform property. For example, on Chrome, elements are taken into its own layer of GPU (RenderLayer), what causes frames to be drawn quicker. If you are interested in understanding it more precisely, try to make a simple animation with position absolute and check "Performance" section in Chrome Developer Tools. Then do the same with transform property. You will see differences in rendering times and GPU layers used to render it. Okay, so we all agree that in that case transform is the best choice. We will stay with the transform property because it will be rendered on a separated GPU layer and we will create a crazy fast opening crawl animation. But you may ask: "Where is this animation mechanism?" Transform on its own just sets specific translation, rotation or scale, STATICALLY, nothing more. To make it move we need animation property - next fancy CSS feature. And this is quite tricky and complex feature. With animation property we can define duration, delay, name, iteration count, direction, even timing function of animation! But to simplify, we will leave most of these properties default. Without more ado, let's make it moooove. Add this code into the <p> styles: [css]animation: animateCrawl 20s[/css] We declared that this block will use animation called "animateCrawl" and it will take 20 seconds. But we still didn't define this animation and to do this we need to fill our animation's keyframes. [css] @keyframes animateCrawl { 0% { transform: rotateX(30deg) translateY(400px); } 100% { transform: rotateX(30deg) translateY(-300px); } } [/css] For those who don't understand what does this syntax mean - first of all we need to enter keyframes expressed in percents. Then we define object's styles in these moments. Animation mechanism will automagically interpolate the difference in time what causes the animation effect. In result, our block of text will move according to Y axis, from the bottom to the top. If you are observant, you may see that I also added rotateX value into transform property, which was added before. At the beginning of my frontend adventure I was asking myself: "Why can't I use only translation here if I want to animate only translation?". Answer is simple - the consequence. CSS treats every style as a pair - property name and value. Transform is just a single property name, so if you want to make a few transformations, you must put them all into this transform style. If you type only translateY transformation, you will simply override previously added rotateX with a default value. Not very comfortable but we must accept what CSS gives us. If you launched this animation, you may see a small problem - at the beginning the movement is really fast and it slows down after every frame. From physical perspective it is done properly but original Star Wars opening crawl was moving uniformly. To fix it we need to change animation timing function. It gives us a lot of freedom, we can even define our own cubic bezier function! To simplify we will use built-in timing function called "ease-in". Mechanism of "ease-in" is pretty easy to understand - every frame animation is faster than frame before. And this is exactly what we need. Perspective gives an impression of slowing down, "ease-in" function accelerates in time so in result we get steady movement. Of course, if you want to make it very precisely, you should consider using custom cubic bezier function. [css]animation: animateCrawl 20s ease-in;[/css] And it's done. Star Wars opening crawl. Now you can use this knowledge for various CSS animations. If you want to see working solution, check this codepen: https://codepen.io/Mossar/pen/rGpWqX
9 października 2017
1
School of New Media
Już w najbliższą sobotę, czyli 6 lutego, Katowice staną się stolicą polskiego marketingu internetowego! Równo o 10:00 rusza inauguracja szkoły "School of New Media", a wraz z nią stanowisko naszej firmy. Zapraszamy do Międzynarodowego Centrum Konferencyjnego, bo itSilesia znajdzie się w doborowym towarzystwie ekspertów, którzy nasycą cały dzień prelekcjami. Więcej informacji o wydarzeniu znajdziecie na stronie http://www.schoolofnewmedia.pl/ oraz fanpage'u https://www.facebook.com/schoolofnewmedia/.
4 lutego 2016
0
Kursor Na Marketing
10 lutego do katowickiego Centrum Biznesowego OPOLSKA 22 zawita konferencja "Kursor Na Marketing" o marketingu internetowym. Sukces pierwszej edycji dowiódł zapotrzebowania na wiedzę o skutecznym podboju internetu, a profesjonalny charakter wydarzenia sprawił, że na kolejnej edycji nie może nas zabraknąć! O 12:00 odbędzie się prelekcja Łukasza Lipki o rozwiązaniach mobilnych w marketingu. CEO itSilesii przedstawi case study rozwoju firmy na podstawie naszych flagowych produktów mobilnych. Zarezerwujcie sobie cały dzień, bo wydarzenie nasycone jest prelekcjami i warsztatami ekspertów! Po więcej informacji zapraszamy na stronę http://www.kursornamarketing.pl/ oraz fanpage https://www.facebook.com/KursorNaMarketing/.
4 lutego 2016
1
SpreadIT 2015
Za nami III edycja popularnej konferencji SpreadIT. Przypominamy, że w tym roku itSilesia została po raz pierwszy brązowym sponsorem tejże konferencji. Nasze stoisko czynne przez cały czas trwania wydarzenia, przyciągało mnóstwo osób, które pytały nas o najnowsze technologie z dziedziny grafiki 3D czy Webu. Z ciekawością testowały również działanie nowoczesnych urządzeń, które powoli pojawiają się na rynku i pozwolą na przeniesienie doznań z obecności w świecie wirtualnym na nowy, wyższy wymiar. Cieszymy się bardzo z tak ogromnego zainteresowania tematyką, którą zajmujemy się na co dzień i zapraszamy chętne osoby do współpracy!
27 listopada 2015
0
itSilesia brązowym sponsorem na konferencji SpreadIT!
Firma itSilesia została brązowym sponsorem na zbliżającej się wielkimi krokami popularnej konferencji SpreadIT!!! Zapraszamy Was gorąco do odwiedzenia 21 listopada Politechniki Śląskiej w Gliwicach. Będziecie mogli wysłuchać ciekawych wystąpień znanych i popularnych prelegentów. W międzyczasie jeszcze gorącej zapraszamy do odwiedzenia naszego stanowiska, gdzie będziecie mogli zobaczyć i wypróbować jak działają najnowsze urządzenia i technologie związane z grafiką 3D. Przyjdź, zobacz, gwarantujemy, że nie pożałujesz! Więcej informacji o konferencji SpreadIT na stronie: http://spreadit.pl/
12 listopada 2015
0
itSilesia na Industriadzie
W sobotę 13 czerwca nasza firma wzięła udział największym festiwalu technicznym na Śląsku - Industriada Święto Szlaku Zabytków Techniki. Nasze stanowisko znajdowało się pod radiostacją Gliwicką - najwyższej drewnianej konstrukcji na świecie. Zaprezentowaliśmy zbudowany przez naszego pracownika prototypowy robot-samochodzik sterowany za pomocą czujnika Kinect lub za pomocą aplikacji na telefonie. Odwiedzający mieli możliwość zmierzenia się z tym niecodziennym sterowaniem pokonując zawiły labirynt, co było wspaniałą rozrywką dla najmłodszych uczestników imprezy. Atrakcja cieszyła się dużym zainteresowaniem. Dziękujemy wszystkim za odwiedzenie nas.
15 czerwca 2015
0
Nasz projekt na targach ISSA
Nasz najnowszy produkt - zestaw aplikacji opracowanych na zamówienie firmy PPUH VOIGT Sp. z.o.o. mających za zadanie wesprzeć dział sprzedaży na targach ISSA INTERCLEAN 2015. ISSA INTERCLEAN to międzynarodowe targi, które odbyły się 22-24 kwietnia 2015 roku w Warszawie w International Expo Center, skupiające przedstawicieli wiodących firm produkujących profesjonalne środki czyszczące oraz wszelkiego typu akcesoria. Głównym celem naszego opracowania było przybliżenie oferty produktowej firmy Voigt i przedstawienie jej w atrakcyjny dla oka sposób. Aplikacje zostały przygotowane w trzech różnych wersjach: - aplikacja 3D prezentowana na 120 calowej ścianie wielodotykowej, pozwalająca na jednoczesną interakcję 4 osobom na raz - aplikacja 3D przeznaczonej na 55 calowy ekran dotykowy, przygotowana dla pojedycznego użytkownika - aplikacja mobilna wykorzystująca mechanizm Rozszerzonej Rzeczywistości (ang. Augmented Reality) przeznaczona na systemy iOS i Android Przygotowane przez nas narzędzia pozwalały potencjalnym klientom poznać zastosowanie wybranych środków czyszczących, ich cechy i parametry oraz błyskawicznie wyliczyć koszty ich użycia. Więcej informacji technicznych i zdjęć znajdziecie na stronie produktów VoigtAR oraz Voigt w zakładce portfolio.
24 kwietnia 2015
0
Kalendarz Kopex wygrywa na festiwalu ZoomArt
Z wielką radością informujemy, że kalendarz Kopex wraz z towarzyszącą mu aplikacją rozszerzonej rzeczywistości, którą stworzyła nasza firma, zwyciężył na Międzynarodowym Festiwalu i Konkursie Kalendarzy Zoom Art, który odbył się 14 marca 2015 roku w Katowicach. Kalendarz okazał się bezkonkurencyjny w kategorii F (kalendarz inny/nietypowy/innowacyjny). Jury doceniło pomysł na multimedialny produkt będący połączeniem rysunków wykonanych tradycyjną techniką przez podopiecznych Fundacji Iskierka z innowacyjna technologią Augmented Reality, która uwalnia dodatkowy wymiar percepcji wyświetlając modele 3D na kartach danego miesiąca. Dodatkowo podczas trwania gali finałowej, goście głosowali na najlepszy kalendarz publiczności - ta statuetka również trafiła w ręce firmy Kopex. Szczegółowe informacje zarówno o tym projekcie jak i innych związanych z rozszerzoną rzeczywistością znajdziecie w naszym portfolio. Więcej informacji o Międzynarodowym Festiwalu i Konkursie Kalendarzy Zoom Art można znaleźć na stronie tego projektu. Na zakończenie jeszcze raz gratulujemy wszystkim zaangażowanym w powstanie projektu oraz wyróżnionym w pozostałych kategoriach.
23 marca 2015
0
Wirtualne lustro dla muzeum w Chorzowie
Nasza wirtualna przymierzalnia została niedawno udostępniona do użytku dla odwiedzających Muzeum "Górnośląski Park Etnograficzny w Chorzowie". Urządzenie pozwala zwiedzającym przyodziać stroje charakterystyczne dla danego regionu Śląska, a także spróbować swoich sił w grach tematycznych. Zdjęcia w ubraniach ludowych zrobione za pomocą wirtualnego lustra można natychmiast wysłać pocztą elektroniczną - pokaż znajomym jak prezentujesz się w tradycyjnym śląskim przyodziewku! Mamy nadzieję, że odkrywanie zakamarków Magicznej Szafy sprawi Wam tyle przyjemności co nam tworzenie jej! Więcej szczegółow technicznych (i zdjęć) znajdziecie na stronie projektu wirtualne lustro w zakładce portfolio.
20 marca 2015
0
[SNEAK PEEK] Wizualizacja obwodnicy Nysy
Kolejny, aktualnie powstający, ciekawy projekt, który chcielibyśmy Wam przedstawić to wizualizacja obecnie budowanej obwodnicy miejscowości Nysa. (więcej…)
17 lutego 2015
0
[SNEAK PEEK] Wirtualna przymierzalnia
Tym wpisem inaugurujemy nowy cykl wpisów, w których będziemy przedstawiać ciekawe projekty, które są tworzone u nas w firmie. Część programistów i artystów działu 3D pracuje aktualnie nad wirtualną przymierzalnią dla Górnośląskiego Parku Etnograficznego w Chorzowie. Dzięki naszym wysiłkom zwiedzający będą mieli możliwość przymierzenia strojów regionalnych męskich i żeńskich z regionu cieszyńskiego, beskidzkiego, pszczyńskiego i rozbarskiego. Więcej szczegółów już wkrótce! Śledzenie ruchu i rozszerzona rzeczywistość to tylko niektóre technologie, które stosujemy w naszych projektach. Zainteresowanych zapraszamy do naszego portfolio.
2 lutego 2015
0
Praktyki Web
Dobrze wiadomo, że nie ma lepszej metody nauki niż zdobycie wiedzy na praktykach. Nasza firma ma wieloletnie doświadczenie w realizacji projektów webowych dla międzynarodowych klientów, a teraz zaprasza właśnie CIEBIE na praktyki! (więcej…)
22 stycznia 2015
0
Praktyki Unity3D
Wszystkich studentów informatyki zainteresowanych grafiką 3D, aplikacjami interaktywnymi i nowymi technologiami oraz chcących zapoznać się z środowiskiem Unity3D zapraszamy na praktyki studenckie w naszej firmie. (więcej…)
22 stycznia 2015
0
Życzenia świąteczne
Z okazji zbliżających się Świąt Bożego Narodzenia chcielibyśmy złożyć Wam życzenia:
spokojnych, śnieżnych, niezwykle rodzinnych i ciepłych najbliższych dni oraz garści dobrych pomysłów i nowych technologii w Nowym Roku.
Wesołych Świąt!
22 grudnia 2014
0
Nowe technologie w edukacji
Jutro tj. środa 10.12.2014 w godz. 9:30-16:30 będziemy prezentować zalety nowych technologii w edukacji podczas konferencji "Edu IT. Nowe technologie w edukacji" w Centrum Edukacji i Biznesu "Nowe Gliwice" przy ul. Bojkowskiej 37 w Gliwicach
W programie konferencji m.in. prezentacja treści e-podręcznika, który w roku 2015 zostanie wdrożony do szkół (prowadzenie - przedstawiciele Ośrodka Rozwoju Edukacji), warsztaty umożliwiające poznanie nowych, bezpłatnych, cyfrowych narzędzi, takich jak: media społecznościowe, otwarte zasoby edukacyjne itp., warsztaty z zakresu zarządzania e-szkołą, wskazanie źródeł finansowania sprzętu i narzędzi cyfrowych dla szkół i placówek oświatowych w nowej perspektywie finansowej 2014-2020.
Szczegółowe informacje:
http://www.ssm.silesia.pl/konf
9 grudnia 2014
0
Warsztaty Grails
W środę 3 grudnia o 18:00 w sali 623 AEiI będziemy prowadzić warsztaty w ramach spotkań Studenckiego Koła Naukowego Inżynierii Programowania i Inżynierii Językowej. Prowadzić je będzie Łukasz Tenerowicz - nasz software developer. Podczas zajęć powiemy sobie o co w ogóle chodzi z tymi Grailsami, dlaczego są fajne, stworzymy prostą aplikację, zobaczymy jak tu działa MVC oraz jak się pracuje w Grailsach z ORMem, Dependency Injection, Groovy Server Pages.
1 grudnia 2014
0
Rarytasy dla aktywnych
Snowcookies rzucają wyzwanie pączkom o tytuł najpopularniejszych polskich ciastek i zdecydowanie nie stoją na straconej pozycji. Zdobędą serca zimowych sportowców, pozwalając na monitorowanie własnego zdrowia oraz osiągnięć. No i nie tuczą. Więcej informacji: http://antyweb.pl/snowcookie-lakomym-kaskiem-dla-fanow-sportow-zimowych-polacy-znow-udowadniaja-swoja-innowacyjnosc/ http://tech.wp.pl/kat,1009783,title,Polacy-w-swiatowym-finale-konkursu-Make-it-Wearable,wid,16954563,wiadomosc.html?ticaid=113a08&_ticrsn=3
15 października 2014
0
Miasteczko multimedialne MediaTent
Zapraszamy wszystkich miłośników nowych technologii do odwiedzenia naszego stanowiska w plenerowym miasteczku multimedialnym MediaTent. Impreza odbędzie się w najbliższy piątek tj. 19 września na Placu Krakowskim w Gliwicach, w godzinach 10.00 - 20.00.
Organizatorami wydarzenia są Miasto Gliwice oraz Śląska Sieć Metropolitalna. Zwiedzający będą mogli zapoznać się z nowościami sprzętowymi oraz aplikacjami multimedialnymi. Wraz z organizatorami przygotowaliśmy aplikację prezentującą najnowsze technologie (augmented reality, iBeacons) w formie gry plenerowej. Więcej informacji tutaj. Na naszym stoisku zwiedzający będą mogli przetestować nasze aplikacje na stole dotykowym, urządzeniach mobilnych (szczególnie technologia augmented reality) oraz Oculus Rift.
Zapraszamy - strefa technologii i druku 3D, stanowisko nr 7.
Wstęp jest bezpłatny.
17 września 2014
0
Odświeżony wizerunek
Jak mawia klasyk: "siadamy głęboko w fotelach, zapinamy pasy i startujemy". Nie mamy tylko wstęgi do przecięcia, dlatego kolejny rozdział w historii firmy upamiętniamy inauguracyjnym wpisem. itSilesia przeszła metamorfozę i powraca z nowym wizerunkiem. Przeprowadziliśmy gruntowną zmianę: rebranding marki zbiegł się z remontem budynku i nowymi siłami w zespole. Liczymy, że nowa szata graficzna strony internetowej i firmowych materiałów ułatwi dostęp do wszelkich interesujących Państwa informacji.
25 sierpnia 2014
0
0