• This is a usual time of the year for summaries so let’s keep the tradition alive and write one. Here’s my 2017 achievements split between months. January Blogging for 18 days straight – nothing near gutek’s achievement (whole year!) but still a nice streak Microsoft MVP title (thx Konrad Kokosa for pointing this one ;)) February […]

  • “Advent of Code is a series of small programming puzzles for a variety of skill levels.” Each day was a fun an interesting coding challenge. I’ve decided to practice and code this in python to learn the skill. Some of them might not be the best python scripting as I was short on time in […]

  • Some time ago I’ve attended a .net developer days 2017 conference. I was quite busy since (traveling, teaching .net, working) that only now I got some time to share some thoughts about it. As a bonus I’m including a short interview I did during the event. I need to state here, just to be clear, […]

  • We developers don’t like documentation. We don’t like to read it, and we even more we don’t like to write it. But sometimes it is worth to read it. Like when you find out that by using datetime in SQL DB you got a milliseconds precision but only if it ends on 0,3,7 (link). That […]

  • In the previous post we’ve removed some of the technical debt that could be found in our NetDeveloperPoland Website application. In this one we will remove it even more. We can even maybe reach a B? Let’s see where we’ll end up at the end of this part.

Nie, to nie będzie ogłoszenie w którym będę poszukiwał programisty. Wpis ten jednak będzie traktował o zatrudnianiu tychże.

Ostatnio coraz częściej ocieram się o jakieś oferty pracy i naszło mnie kilka przemyśleń, którymi chciałbym się tu podzielić. Od razu zastrzegę, że są to tylko i wyłącznie moje opinie, a że za wyrocznię rynku się nie mam, mogą się kompletnie nie zgadzać z tym co mają do powiedzenia moi koledzy (i koleżanki?) programiści.

Oferta ofercie nie równa

Wiadomo, że ile firm tyle ofert. Każda z nich stara się wyróżnić na tle konkurentów i przyciągnąć do siebie najlepsze umysły. Kiedyś w wielu poradnikach dla osób szukających pracy można było przeczytać, że rekruter ma twoje CV tylko przez kilka sekund i jeśli w tym czasie go sobą nie zainteresujesz to przepadłeś. Oczywiście jest to zbyt wielkie uproszczenie – rekruterzy trochę baczniej przeglądają twoje CV niż przez kilka sekund – w końcu jeśli uda im się dopasować kandydata zdobędą prowizję. Prowadzi to jednak czasem do odwrotnych sytuacji i prób wciśnięcia kandydata na siłę na dane stanowisko, mimo że mu ono nie odpowiada a co gorzej nie pasuje do jego kwalifikacji. 
W wielu przypadkach przy zatrudnianiu programistów jest odwrotnie – to rekuruter ma tylko kilka sekund aby przykuć uwagę developera do swojego ogłoszenia. Jeśli będzie to standardowe ogłoszenie o pracy w “młodym i dynamicznym zespole” z “elastycznymi godzinami pracy” to przepadł. Oczywiście o ile programista nie szuka w chwili obecnej desperacko pracy…
Skoro już wiemy jak można sobie zaprzepaścić drogę do serca developera to teraz spróbujmy sobie odpowiedzieć na pytanie co zrobić aby do niego dotrzeć. Prosta sprawa – wyróżnij się!

Wyróżnij się

Podstawy podstaw…

Nawet nie będę wspominał o takich podstawach jak to, aby ogłoszenie było adekwatne do kwalifikacji bo to powinna być oczywista oczywistość. Jednakże zdarzają się ogłoszenie gdzie ktoś szuka programisty C++ z 6 letnim stażem, a ja mogę pochwalić się ino rocznym i to było dawno. I nieprawda :). Prym w tego typu ogłoszeniach wiedzie goldenline – jak widzę w swojej skrzynce ogłoszenie, którego autorem jest goldenline – wiem, że będzie to stracony czas. Ogłoszenia wyglądają jak wysyłane przez robota, więc przynajmniej nie marnuje się praca jakiejś ludzkiej istoty. Jeśli to robi człowiek – shame on you GL. Robot nie robot, dostaję też ogłoszenia podobnie niedopasowane od rekruterów z krwi i kości – panowie i panie – ja wam ładnie odpisze, że nie jestem zainteresowany, ale na prawdę szkoda naszego wspólnego czasu. C++ to nie to samo co C#.

Widły, widły komu widły…

Skoro już wiemy, że ogłoszenie powinno być adekwatne zastanówmy się czym jeszcze możemy zabłysnąć. Słowo magia – widełki. Jeśli tylko macie taką możliwość podajcie je – oszczędzi to czasu i frustracji obu stronom. Nic tak nie popsuje wam pracy jak opinia jakiegoś znanego na rynku programisty, który przemierzył pół Polski, żeby dowiedzieć się, że będzie pracował za pół czy mniej tego co obecnie*.
Jeśli nie możecie ich podać – ok – napiszcie, że wstępne ustalenia finansowe odbędą się podczas pierwszej rozmowy telefonicznej i tego się trzymajcie. Dzięki temu od razu rozwiąże się sprawa czy programista jest zainteresowany czy nie.

* – tu mogą pojawić się głosy, że są pasjonaci, dla których sprawa pieniędzy nie jest istotna. True, ale niestety oferty, które będziecie drodzy Państwo rekruterzy przesyłać to nie jakieś oszałamiające, super-nowoczesne projekty tylko zwykłe, szare ale projekciki ale także potrzebne.

Logika, logika, logika…

Mając już widełki, jeśli obawiacie się, że mogą one nie być kuszące należy podejść do programisty uderzając w jego miłość do zagadek i ciekawych zadań. Tu ostatnio dobry przykład dało Inteligo – co prawda oferta nie na programistę a administratora, ale ideę chyba będzie można prosto wyłapać. Ogłoszenie jest tu – http://www.pracuj.pl/praca/administrator-systemow-web-warszawa,oferta,2584268. Widzicie tam coś interesującego? Ten ciąg aHR0cDovL2ludGVsaWdvLnBsL2l3YW5hY29va2llCg== spowodował, że kilkadziesiąt osób komentowało i starało się rozwiązać łamigłówkę przygotowaną przez rekurtera – zobaczcie sami – (choć tu nie obyło się bez pomocy osób trzecich – niebezpiecznika oraz tego, że na pewno pomagał jakiś dev/admin – mało chyba, który rekurter zna języki funkcyjne). Dzięki takiemu prostemu zabiegowi udało się zatrzymać uwagę potencjalnych kandydatów na trochę dłużej. No dobra – może to nie takie proste, bo w końcu ktoś to musiał przygotować, ale tak czy inaczej stosunek pracy do efektu jest chyba na plus. Inna oferta pracy z zagranicy – GCHQ czyli brytyjski wywiad rekrutował (ok, wstępnie) za pomocą ciekawego zadania umieszczonego na stronie – http://www.canyoucrackit.co.uk/

Jak do tej pory nie spotkałem, się z ogłoszeniami dla programistów, które zawierałyby podobne zadania. Sądzę, że byłaby to ciekawa odmiana od ogłoszeń, które obecnie widzi się tysiącami.

Oczywiste, oczywistości…

Sprawa jasna, że czasem chcemy/potrzebujemy napisać o rzeczach, które w ogłoszeniach piszą wszyscy. Jak wybrnąć, żeby nie tworzyć kolejnego ogłoszenie o “młodym i dynamicznym zespole”? A no można napisać to trochę na około…

Zamiast pisać, że zapewniacie prace:

  • w “młodym i dynamicznym zespole” – można napisać…. nie to naprawdę nie jest coś, czym się warto chwalić. Można to pominąć 🙂
  • na “nowoczesnym sprzęcie” – podajcie ile FPSów wyciąga komputer programisty w najwyższej rozdzielczości na dwóch monitorach w FarCry 3. O i od razu macie informację, że wasi programiści pracują na dwóch monitorach – bo przecież dbacie o nich i mają najnowocześniejszy sprzęt. 
  • “elastyczne godziny pracy” – pokażcie wykres dobowy aktywności waszych programistów, z niego jasno będzie wynikać czy firma pozwala pracować w różnych godzinach czy to tylko pusty frazes
  • z “możliwością rozwoju zawodowego” – wypiszcie 2,3 szkolenia na jakich byli wasi programiści w przeciągu ostatniego roku, albo lepiej – jeśli macie w waszym zespole jakiegoś wymiatacza (postać dość dobrze znaną w środowisku) – napiszcie, że będzie można z nim kodować w parach! Uciecha po pachy dla każdego prawdziwego programisty. Podajcie do jakich książek będzie miał dostęp nowozatrudniony programista – a jeszcze lepiej napiszcie, że macie licencję Pluralsight albo wykupiony dostęp do Safari Books online.

I tak można z większością tych punktów w sekcji Oferujemy….

Trochę przydługi wyszedł mi wpis i mam nadzieję, że nie zgubiłem głównej idei. Rynek jest ciężki – dużo ofert, trochę mniej dobrych programistów…trzeba działać.

Na zakończenie pytanie do was bracia/siostry koderzy? Co sądzicie o takich sposobach? Ma to sens?


dotnetomaniak.plNajciekawsze artykuły o .NET


Dziś wpis bardziej jako informacji dla samego siebie, aby pamiętać na przyszłość, ale być może przyda się także komuś innemu.
Jakiś czas temu musiałem trochę podziałać z ciasteczkami i okazuje się, że HttpCookieCollection ma “ciekawą” właściwość, które może czasem przyprawić o chwilę pomyślunku :).

Okazuje się, że dodając ciastka do kolekcji możemy mieć wpis o takiej samej nazwie wielokrotnie. Więc jeśli kiedykolwiek zobaczymy, że po usunięciu jakiegoś ciasteczka nadal je w naszej kolekcji widzimy to sprawdźmy czy przypadkiem nie ląduje tam ono wielokrotnie.
Jeśli chcemy mieć pewność, że będziemy mieć tylko jedną kopię ciasteczka bezpieczniej skorzystać jest z metody Set. Sprawdza ona czy ustawiane przez nas ciastko nie zostało już utworzone i jeśli zostało to aktualizuje informacje w nim zawarte.

Dzięki temu mamy pewność, że nie okaże się, że po usunięciu jakiegoś ciastka nadal będzie ono w kolekcji. Zupełnie jakby zjeść ciasto i mieć ciastko…

Zdjęcie: By Sarah Cady

Nie minęło jeszcze 3 dni od mojego posta o 2.0 wersji IlSpy a okazuje się, że niedawno także inny dekompilator miał “premierę”. JetBrains wypuścił wersję beta swojego narzędzia – dotPeek.

Najnowsza Beta dotPeek’a została także wyposażona w listę assemblies. Działa ona jednak trochę inaczej niż w przypadku IlSpy’a. Tutaj zapisujemy listę do pliku, który później możemy wskazać do odczytania. Brakuje już zapisanych list przez to, gdy wyczyścimy sobie assemblies z .NET Framework’a trzeba będzie je ponownie dodawać ręcznie.
dotPeek “nie radzi” sobie jeszcze z ładniejszym wyświetlaniem operatorów dla typów Nullable.

Podobnie jest z ExpressionTrees. Poniżej porównanie dekompilacji kodu w IlSpy oraz dotPeek.

U góry dotPeek. Dolny fragment – ilSpy

Miłą opcją w dotPeek’u jest obsługa spakowanych formatów – zip, nupkg (NuGet). Dzięki temu będziemy mogli przejrzeć kod naszych bibliotek bez potrzeby ich rozpakowywania.
dotPeek, podobnie jak wersja 2.0 IlSpy’a (choć nie wspomniałem o tym przy okazji artykułu o nim) potrafi dekompilować pliki .winmd, które będą nieodłączną częścią Windows 8 i WinRT.

Zachęcam do wypróbowania i wybrania swojego ulubionego narzędzia do dekompilacji.

O IlSpy’u pisałem już przy okazji przeglądu narzędzi do dekompilacji (Show me your code–przegląd narzędzi do podglądania) a okazało się, że na dniach (dokładnie 15.04) została wydana wersja 2.0 tego narzędzia. Zobaczymy co się zmieniło w stosunku do poprzedniej wersji.
UI nie zmienił się w stosunku do poprzednich wersji dzięki temu nadal jest minimalistyczny – ale w pełni funkcjonalny.

W najnowszej wersji doszła możliwość tworzenia list. Dzięki niej będziemy mogli zgrupować sobie assemblies i później za pomocą podwójnego kliknięcia załadować wszystkie niezbędne pliki, które chcemy móc przeglądać. Całkiem wygodne, dla osób, które pracują z różnymi zestawami assemblies a nie chcą mieć zaśmieconego widoku głównego – aplikacja na start ma już kilka zdefiniowanych list: .NET 4 (WPF), .NET 3.5, ASP.NET (MVC3).

Dodatkowo sam silnik dostał wsparcie dla Expression Trees oraz Elevated operators, dzięki temu pokazany kod jest prostszy do odczytania.
Dla przykładu kod:

Code Snippet
  1. int? z = 12;
  2. int? w = 5;
  3. int? x = z + w;
  4. Console.WriteLine(x);

W obecnej wersji będzie dekompilowany jako:

gdzie w poprzedniej wersji musieliśmy się zadowolić następującym:

Przydatne usprawnienia. Mam nadzieję, że teraz jeszcze więcej osób skorzysta z tego narzędzia. A Ty jakiego używasz dekompilatora dla .NET?

Dla uważnych czytelników poprzedniego wpisu należą się przeprosiny i wyjaśnienia. Napisałem w nim, że pokaże dwa sposoby zobrazowania jak wygląda protokół naszej płytki a przedstawiłem tylko jeden – analiza w IDA. Nie wiem czy to z pośpiechu czy jakiś inny czynnik na to wpłynął, ale o tym drugim – po prostu zapomniałem. Teraz zatem, bijąc się w pierś, omówienie drugiej metody.
Jak wspomniałem w tymże wpisie – istnieją aplikacje, które pozwalają nasłuchiwać i wyświetlić nam całą komunikację z wybranym portem COM. Jest kilka darmowych, kilka płatnych – na pewno każdy wybierze coś dla siebie.
Ja użyłem Serial Port Monitor’a od Elitma Software, ale jako, że był to trial i już wygasł to nie pokażę, jak to w nim wyglądało. Zamiast tego użyję darmowego Serial Port Monitora (wow, jaka różnorodność nazw :]) od firmy HHD Software.

Po zainstalowaniu i uruchomieniu mamy możliwość wyboru trzech z rodzajów monitoringu, ale w wersji bezpłatnej dostępny jest tylko Serial Port Monitor i ten zaznaczamy.

Następny krok to wybór numeru portu, na którym będziemy nasłuchiwać a na sam koniec zostanie wybór sposobu prezentacji danych. Wydaje mi się, że najprzydatniejszym jest połączenie widoku tabelarycznego z Request View. Ten pierwszy pokaże nam wszelkie komunikaty IOCTL a drugi na wyższym poziomie pokaże jakie bajty został wysłane. Mając tak przygotowaną aplikację wysyłamy przez naszą wzorcową aplikację jakiś łańcuch znaków do płytki i obserwujemy wynik.

Dzieje się sporo – to widać. Szczególnie na widoki Table View przelatuje sporo komunikatów IOCTL nawet jak weźmiemy tylko te z kierunkiem DOWN (UP to potwierdzenie odebrania – ktoś zna się bardziej na tym?) to trochę tego zostanie.
Na drugim widoku jednak mamy bajty, które zostały wysłane i na nich możemy się skupić, aby przeanalizować transmisję i spróbować dopasować do tego co dowiedzieliśmy się poprzednim razem.

Widzimy wyraźnie, że na początku wysyłany jest bajt 0 (reset?). A następnie zaczynają się dane właściwe – podział na 4 pakiety jest wyraźnie widoczny jako, że wszystkie zaczynają się tą samą sekwencją: 02 31 06 a następnie podane jest przesunięcie (można to traktować jako nr pakietu << 6) czyli będzie to wartość 00, 40, 80, C0. Dalej już są różnice – w pierwszym pakiecie przesyłamy informacje o użytym efekcie oraz prędkości. Dalej pozostają nam już tylko dane + suma kontrolka widoczna na samym końcu każdego pakietu. Na sam koniec transmisji “stopka” i gotowe.

Czy to bardziej intuicyjne niż IDA? Pewnie tak – nie wymaga znajomości assemblera i pozwala w wizualny sposób zorientować się jaka komunikacja jest przeprowadzana z urządzeniem. Czy można by tylko na tej podstawie poznać protokół? Raczej nie – nie wiem czy można domyślić się, że 1A to suma kontrolna pierwszego pakietu a 77 drugiego. Co obrazują te 3 bajty na końcu?
Być może dla osób z jakimś doświadczeniem będzie to oczywiste – będą one wiedzieć co w takim protokole powinno być i doszukają się w tych danych odpowiedniego znaczenia.
Na pewno dobrym rozwiązaniem jest narzędzie + IDA. Tyle w kwestii wyjaśnień.

W poprzednim poście zapoczątkowałem artykuł odnośnie oprogramowania panelu LED, którą zakupiłem jakiś czas temu. W dzisiejszym poście spróbujemy rozkodować cześć dotyczącą wysyłania informacji do płytki. Spróbujemy poznać protokół jaki obsługuje nasz kawałek hardware’u. Na początek jednak jestem winny wam trochę wyjaśnienia.

W ostatnim wpisie zdekodowaliśmy sobie kawałek procedury, która odpowiada za wykrycie portu do którego podłączona jest płytka, ale tak na prawdę nie pokazałem, gdzie na UI jest uruchamiana dana funkcja. Porównajcie sobie obrazek z poprzedniego wpisu i poniższy.

Tak, tak. Okazuje się, że UI jest dość specyficznie napisane i jego część jest ukryta (kto wie po co?). Po rozwinięciu okna ukazuje się jeszcze kilka przycisków w tym jeden (Initial Badge), którego procedurę zanalizowaliśmy ostatnim razem. Ciekawy sposób na interfejsu użytkownika.

Dziś zajmiemy się drugą funkcją, która faktycznie powoduje zapis do urządzenia tekstu. Wywoływana jest po naciśnięciu przycisku Send.

Patrząc na UI widzimy, że panel umożliwia wpisanie do siebie 6 tekstów, które będą wyświetlane a dodatkowo w 7 i 8 “kanale” możemy umieścić logo (te sobie rozpracujemy w którymś z kolejnych wpisów). Dodatkowo możemy sterować prędkością wyświetlania (1. przycisk za tekstem), oraz efektem użytym do pokazania danego tekstu (2. przycisk). Z tego co udostępnia nam aplikacja dostępne jest 5 prędkości oraz 4 efekty (scroll, śnieg, flash, stały tekst).

Analiza

Aby poprawnie wysłać tekst do płytki (czy innego urządzenia) musimy poznać jej protokół. W tym wypadku mamy dwie możliwości do wyboru, podobnie jak poprzednim razem posłużyć się IDA i zobaczyć co jest wysyłane, bądź też skorzystać z programu za pomocą, którego będziemy mogli podejrzeć całą komunikację z wybranym portem COM. Zobaczymy obie metody. Na początek – IDA.

Tym razem skupimy się na drugiej metodzie, w której znaleźliśmy referencję do naszej metody sio_open a mianowicie _TForm1_suitempSendClick. Metoda jest dość obszerna z racji tego, że odpowiada jak już pisałem wcześniej za wszystkie 8 kanałów, które płytka wspiera. Obszerność/złożoność całej metody można zobaczyć na zrzucie obok.
Spory, skomplikowany kawałek kodu.
My jednak skupimy się tylko na wybranym fragmencie. Ponownie odszukujemy sobie wszystkie odwołania do sio_open i przechodzimy do pierwszego tego w naszej metodzie (powinny być 2). Jest tam standardowe otwarcie portu i ustawienie jego parametrów (prędkość, bity, parzystość, etc.) a następnie wysyłamy do płytki 0 (reset?). Dalej sporo kodu, ustawiającego progressbar’y na UI (pomijamy) i związanego z kanałem 7 i 8 więc na razie to pomijamy. Dochodzimy jednak do następującego fragmentu.

Widzimy tu początek tworzenia ramki/pakietu, która będzie wysyłana do urządzenia. W zmiennej var_D8 przechowywany jest br kanału, var_B4 to kolejny numer paczki (będzie o tym jeszcze dalej). 
Nasz nagłówek po wykonaniu tego pierwszego fragmentu kodu wygląda następująco (hex): 02 31 06 00, gdzie: 02 31 – stały identyfikator. 06 – numer kanału + 6, 00 – numer kanału * 64 (offset danych) W drugim fragmencie w pętli zostaje przepisane maksymalnie 64 znaki z naszej wiadomości do pakietu z czego pierwsze 4 stanowią dodatkowe informacje dla płytki. Jeśli więc nasza wiadomość to abc (0x61, 0x62, 0x63) to w buforze znajdą się następujące dane do wysłania. 0x35 0x31 0x41 0x03 0x61 0x62 0x63, gdzie 0x35 – prędkość, 0x31 – numer kanału w ASCII (ponownie?) oraz 0x42 – efekt (innym razem opiszemy co robią poszczególne). Każde 64 znaki z nagłówkiem (czyli 68 znaków) to jedna paczka. Aplikacja wysyła 4 paczki (dodatkowe dane znajdują się tylko w pierwszej).

Jednak to nie wszystko. Gdybyśmy wysłali tylko tyle, aplikacja uraczyłaby nas pięknym czerwonym XXX (hmm – jakaś tajna funkcja :]). Aby uzyskać OK (też czerwone) – musimy zrobić jeszcze jedno (a w zasadzie jeszcze 2 rzeczy). Jak przejdziemy z kodem niewiele poniżej fragmentu, który widzieliśmy powyżej zobaczymy taki fragment kodu:

var_18C wskazuje na nasz bufor danych a eax służy w tym wypadku za indeks. Iterujemy się po 68 znakach bufora zaczynając od 2 (eax = 1) i liczymy sumę % 255 czyli prosta suma kontrolna. Tę dodajemy na koniec naszej ramki danych.
Podsumowując więc tworzymy nagłówek (pierwsze 8 bajtów), w których są informacje o kanale, prędkości, efekcie wyświetlanego tekstu, długość tekstu. Następnie 60 znaków tekstu a na koniec zamykamy to sumą kontrolną. To jednak jeszcze nie koniec….
Gdybyśmy na tym poprzestali, to po zapisie nadal raczyłby nas groźny napis XXX. Jeśli dobrze poszukamy (oczywiście nie trzeba szukać, tylko przeanalizować kod) to przed samym zamknięciem komunikacji z portem COM wysyłane są jeszcze dodatkowe bajty danych. Jak popatrzymy na poniższy fragment kodu

to zobaczymy, że przed ostatecznym wywołaniem sio_close wysyłamy jeszcze 3 bajty: 0x02, 0x33 oraz jakiś bajt znajdujący się w var_A5. Odszukując gdzie jest ustawiany ten bajt trafiamy na następujący kod:

Co nie jest pokazane na tym zrzucie w eax mamy ilość zapisywanych linii (np. 6). Ten prosty kod przekłada się na (0xFF >> (8-eax)). Finito….

Uff.. no to teraz C#.

LEDBoard

Code Snippet
  1. private static byte[] CreatePackage(int lineNo, int packageNo, string text)
  2. {
  3.     byte[] data = new byte[PackageSize];
  4.     var startIndex = (byte) (packageNo << 6);
  5.     data[0] = 0x02;
  6.     data[1] = 0x31;
  7.     data[2] = (byte) (lineNo + 6);
  8.     data[3] = startIndex;
  9.     if (IncludeLineInfo(packageNo))
  10.     {
  11.         data[4] = (byte)Speed.Fast;
  12.         data[5] = (byte)(lineNo + 0x31);
  13.         data[6] = (byte)Effect.Scroll;
  14.         data[7] = (byte)text.Length;
  15.     }
  16.     if (HasMoreData(startIndex, text))
  17.     {
  18.         Array.Copy(text.Select(x => (byte) x).ToArray(), startIndex, data, packageNo == 0 ? 8 : 4,
  19.                    Math.Min(text.Length – startIndex, MaxDataSize));
  20.     }
  21.     data[data.Length – 1] = (byte) (data.Sum(x => x) – 2);
  22.     return data;
  23. }
  24.  
  25. private static bool HasMoreData(byte startIndex, string text)
  26. {
  27.     return startIndex < text.Length;
  28. }
  29.  
  30. private static bool IncludeLineInfo(int packageNo)
  31. {
  32.     return packageNo == 0;
  33. }

Metoda przygotowuje dane do pakietu na podstawie numeru linii, pakietu oraz samych danych. W zależności od tego czy jest, który to pakiet dokładamy informacje o prędkości czy użytym efekcie. Następnie zapisujemy do bufora dane i dopełniamy sumą kontrolną. Odejmujemy od niej 2 jako, że aplikacja liczyła ją ze wszystkich danych bufora z pominięciem początkowego 02.
Film pokazujący wgrane dane do płytki.

Poza udostępnieniem możliwości zapisu w kodzie trochę się pozmieniało. Dodałem ViewModel oraz implementację interfejsu ICommand do obsługi zapisu do płytki.

Zainteresowany dalszą analizą? Zamieść komentarz. Co jest do poprawienia? Czego ma być więcej?

Pierwsza część: LED Board – cz. 1. Jak poprzednio wszelkie zmiany w kodzie odwzorowane są na GitHub’ie – LEDBoard.

W sklepie z tajwańsczyzną zakupiłem sobie jakiś czas temu mały gadżet aby trochę się nim pobawić a jednocześnie coś pokodować. Gadżet ten to niewielki panel LED, który można sobie oprogramować prawie do woli. Co prawda jest do tego dołączona aplikacja za pomocą, której można ją obsłużyć, ale po pierwsze wygląda tak:

Wygląd aplikacji dołączonej do panelu

a po drugie, kto by tam ufał tajwanom. Napiszemy sobie sami :).

Uwaga: Będzie trochę asemblera oraz trochę Pascala. Jak ktoś nie lubi może przejść kawałek niżej, gdzie będzie kod C#, choć wiedza poniżej jest trochę istotna i wynika z niej kod C#, który będzie na dole.

Urządzenie

Samo urządzenie podłączane jest za pomocą USB, ale w systemie widziane jest jako COM więc nie będziemy musieli się zajmować jak wykryć urządzenia HID w systemie (być może zajmiemy się tym w przyszłości). Aby móc cokolwiek napisać dla naszego panelu musimy przede wszystkim poznać jej protokół. Aby to zrobić posłużymy się disassemblerem IDA (samej obsługi dekompilatora raczej będę unikał – jeśli ktoś potrzebuje odsyłam do świetnej książki – The IDA Pro Book), a konkretniej jej darmową wersją 5.0.

Analiza

Po uruchomieniu IDA i wyświetleniu importowanych funkcji widzimy, że aplikacja korzysta z biblioteki
PCOMM i importuje kilka funkcji zaczynających się na sio_* – sugeruje to tak jak już wcześniej było wspomniane użycie COMów do komunikacji.

Wyszukajmy sobie wszelkie odwołania do metody sio_open.

Na szczęście jest ich tylko 4 więc nie będziemy mieli problemów z analizą tych miejsc. Przeglądając pobieżnie _TForm1_suiButton5Click wygląda na prostszą metodę więc od niej zaczniemy.

Kod już na początku tej metody rozdziela się na dwie części a potem spotyka się w jednym punkcie. Fragment, który rozdziela jest następujący:

Pozwoliłem sobie już dodać etykietę co robi ten kod, bo jak za chwilę zobaczymy fragment to właśnie się dzieje.

We fragmencie po prawej widzimy pętlę gdzie powtarzamy ciąg operacji: otwieramy port (sio_open) przekazując w esi numer portu do otwarcia, następnie ustawiamy pewne parametry portu (jakie? – o tym za chwilę) za pomocą sio_ioctl i ustawiamy timeout’y (sio_SetReadTimeouts) dla połączenia. Dalej zapisujemy do portu znak (‘T’ – 54h) i próbujemy go odczytać – jak się udało – mamy nasz port. Jeśli nie próbujemy kolejny port. O ile zrozumienie co jest przekazywane do metody sio_open było dość oczywiste to pozostałe metody mogą sprawić już trochę trudności. Skąd też więc możemy się dowiedzieć co oznaczają wartości przekazane do sio_ioctl? Oczywiście z dokumentacji :D. Pytanie tylko gdzie takową znaleźć?? Z pomocą przychodzą nieprzepastne skarbnice Internatu i stronie hackchina znajdujemy dokumentacji jakiegoś modułu w Pascalu (jeszcze do tego Turbo :]), który metody sio_. Dzięki temu będziemy mogli zrozumieć co się dzieje. Bierzemy pierwszą lepszą metodę, która korzysta z sio_ioctl i paczymy* :].

FLastError:=sio_ioctl(FintPort,Ord(FBaudRate),GetMode);

Widzimy zatem, że pierwszym argumentem jest port (będzie on na stosie rzucany jako ostatni), drugim prędkość a trzecim dodatkowe ustawienia odnośnie przesyłanych danych. Każdy kto na studiach programował coś z COMem od razu powinien przypomnieć sobie jakieś bity danych parzystość i inne :] Zobaczmy jak to wygląda w naszym przypadku. Port pominiemy – jako BaudRate wrzucamy 0Eh czyli 14, ale to raczej nie jest prędkość. 14 b/s nie powala nawet jak na wolny COM :). W Pascal’owej wersji widzimy że tak na prawdę wrzucamy tylko indeks prędkości (Ord) musimy zatem zobaczyć jakie są dostępne.

TBaudRate = (br50,br75,br110, br134, br150, br300, br600, br1200, br1800, br2400, br4800, br7200, br9600, br19200, br38400, br57600, br115200, br230400, br460800, br921600 );

Kilka prędkości dostępnych mamy – na 14 pozycji jest br38400 tak więc z taką prędkością będziemy wysyłać i odbierać dane. Co do wartości ostatniego parametru – 3 to jego wartość określa tryb pracy. Jest to pole bitowe i musimy poznać jego strukturę. Ponownie wszelką wiedzę daje nam dokumentacja:

function TPort.GetMode: Longint;
var
byteDB,byteSB,bytePR:Byte;
begin
byteDB:=0;
byteSB:=0;
bytePR:=0;
case FDataBits of
dbFive:byteDB:=0;
dbSix:byteDB:=1;
dbSeven:byteDB:=2;
dbEight:byteDB:=3;
end;
case FStopBits of
sbOneStopBit:byteSB:=0;
sbTwoStopBits:byteSB:=4;
end;
case FParity of
prNone:bytePR:=0;
prOdd:bytePR:=8;
prEven:bytePR:=$18;
prMark:bytePR:=$28;
prSpace:bytePR:=$38;
end;
Result:=byteDB or byteSB or bytePR;
end;

Widzimy, że odpowiednia wartość budowana jest z 3 wartości: ilości bitów danych (bity 1-0), bitu stopu (bit 2) oraz bitów parzystości (bity 5-3). W naszym przypadku wartość 3 oznacza odpowiednio: 8 bitów danych, 1 bit stopu oraz brak parzystości. Uff. Mając tyle wiedzy chyba możemy przystąpić do napisania kawałku kodu w C#.

.NET

Na szczęście w C# mamy dostępną klasę SerialPort (System.IO.Ports), za pomocą której możemy obsłużyć porty COM.

Code Snippet
  1. for (int i = 1; i <= 8; i++)
  2. {
  3.     var serialPort = new SerialPort(“COM” + i, 38400, Parity.None, 8, StopBits.One) {ReadTimeout = 10};
  4.     try
  5.     {
  6.         serialPort.Open();
  7.     }
  8.     catch (IOException)
  9.     {
  10.         continue;                    
  11.     }
  12.     try
  13.     {
  14.         serialPort.Write(“T”);
  15.         int read = serialPort.ReadChar();
  16.         if ((char)read == ‘T’)
  17.             return serialPort.PortName;
  18.     }
  19.     catch (Exception)
  20.     {                    
  21.         continue;
  22.     }
  23. }
  24. return “None”;

Kod jest niezwykle prosty więc tylko dla porządku. W pętli sprawdzamy COM1 – COM8 poprzez utworzenie obiektu i ustawienie takich samych parametrów jak wzorcowa aplikacja. Ustawiamy też timeout czytania na 10 ms (to samo robi wzorcowa aplikacja, ale nie zostało to pokazane na fragmencie powyżej). Następnie próbujemy otworzyć port a w przypadku wystąpienia wyjątku sprawdzamy kolejny. W przeciwnym razie zapisujemy do portu literę ‘T’ (czemu akurat ‘T’ wysyła apka? – nie wiem) a następnie próbujemy ją odczytać. Jeśli się uda i uzyskamy tę samą literę mamy nasz port. Voila!

Na razie kod nie robi zbyt wiele, ale wszelkie dostępne materiały będę publikował na GitHub’ie – LEDBoard.
Następnym razem analiza drugiej z metod. Spróbujemy zanalizować i napisać program potrafiący wysłać coś do naszego wyświetlacza.

Lubie WPF’a.
Można o nim powiedzieć, że jest niedorobiony, wolny a technologia ta już nie będzie dalej rozwijana przez MS. Można też zachwycać się nad tym jak bardzo wiele jest klas w WPF i jak wiele jest ich jeszcze nieodkrytych kąsków.

Ostatnio natrafiłem na jeden z nich o którym chciałbym dziś napisać: *Bitmap(De/En)coder. Są to klasy, które umożliwiają pracę z różnymi formatami graficznymi np. PngBitmap(De/En)coder, BmpBitmap(De/En)coder, JpegBitnap(De/En)coder, TiffBitmap(De/En)coder.
Kiedyś, we wpisie 70-502 (WPF) – Image Metadata pisałem już o decoderach. Dziś trochę o encoderach.


Generalnie, jak można się domyśleć to co można odczytać za pomocą klas De-, przy pomocy klas En- można zapisać, tak więc w skrócie opisują za pomocą tych klas, można w WPFie stworzyć odpowiednie obrazki. Tyle? Gdyby to było wszystko pewnie bym o tym nie pisał posta (chociaż, kto wie – dawno już nic nie napisałem ;)) – posiadają one jednak ciekawą opcję. Potrafią, w bardzo prosty sposób zapisać w sobie stan kontrolki czyli zrobić zrzut ekranu naszej aplikacji. Sweet :]. Jak możemy ich użyć? Zobaczmy poniżej. Aby móc zapisać cokolwiek w tym obiekcie musimy dodać to do kolekcji Frames. Niestety przyjmuje ona obiekty typu BitmapFrame. Ona natomiast w statycznym konstruktorze bierze BitmapSource. Jak więc za tem możemy za ich pomocą zapisać do pliku nasze okno? Z pomocą przychodzi jeszcze jedna sweet-klasa a mianowicie RenderTargetBitmap.
A poskładane całe do kupy?

Code Snippet
  1. private void SaveScreenshot(System.Windows.Media.Imaging.BitmapEncoder encoder, string name)
  2. {
  3.     RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int) Width, (int) Height, 96, 96,
  4.                                                              PixelFormats.Default);
  5.     targetBitmap.Render(this);
  6.     BitmapFrame frame = BitmapFrame.Create(targetBitmap);            
  7.     encoder.Frames.Add(frame);
  8.     using (FileStream stream = File.Open(name, FileMode.Create))
  9.     {
  10.         encoder.Save(stream);
  11.     }
  12. }

Tworzymy sobie odpowiednich rozmiarów RenderTargetBitmap a następnie renderujemy do niego zawartość naszej formy. Następnie wrzucamy to do frame’a i zapisujemy do pliku. Proste. Tak skonstruowaną metodę możemy sobie użyć z dowolnym decoderam i zapisywać do jakiego formatu nam się podoba.

Code Snippet
  1. private void SavePng(object sender, RoutedEventArgs e)
  2. {
  3.     PngBitmapEncoder encoder = new PngBitmapEncoder();
  4.     SaveScreenshot(encoder, “main.png”);
  5. }
  6.  
  7. private void SaveBmp(object sender, RoutedEventArgs e)
  8. {
  9.     BmpBitmapEncoder encoder = new BmpBitmapEncoder();
  10.     SaveScreenshot(encoder, “main.bmp”);
  11. }
  12.  
  13. private void SaveTiff(object sender, RoutedEventArgs e)
  14. {
  15.     TiffBitmapEncoder encoder = new TiffBitmapEncoder();
  16.     SaveScreenshot(encoder, “main.tiff”);
  17. }
  18.  
  19. private void SaveJpg(object sender, RoutedEventArgs e)
  20. {
  21.     JpegBitmapEncoder encoder = new JpegBitmapEncoder();
  22.     SaveScreenshot(encoder, “main.jpg”);
  23. }

Gotowe. PS. Jak wykonacie poniższy kod to zobaczycie dziwną ramkę po prawej i u dołu. Powodem tego jest fakt, że podstawiamy wielkość całego okna wraz z obramowaniem a renderowana jest tylko zawartość konta. Przez to wymiary faktycznego obrazka są większe niż to co na nim rysujemy.
Miłego renderowania.

Dzisiejsze znalezisko w kodzie:

I kto powiedział, że praca programisty musi być nudna 🙂

W ostatnim poście wspominałem, że napisze jeszcze o ustawieniach debuggera VS w rejestrze.

Okazuje się, że nie wszystko da się ustawić w Visual Studio czy ustawieniach systemowych. Częścią funkcjonalności trzeba sterować za pomocą ustawień w rejestrze – na szczęście są to marginalne przypadki.

Czasami jednak zdarza się, że dodając jakieś wyrażenie do okienka Watch dostajemy niemiły komunikat – Function evaluation timed out. Buu… :/


Okazuje się, że możemy choć trochę wpłynąć na VS po jakim czasie pokaże nam ten komunikat – niestety musimy zrobić to w rejestrze.

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Debugger.

Mamy tam wszelkie ustawienia, których dokonujemy za pomocą VS ale także kilka wartości, których nie odnajdziemy w okienku Settings – to te, których nazwy kończące się na Timeout.

Jak może się domyśleć odpowiadają one za czas po jakim VS rzuci nam wspomnianym wcześniej komunikatem. Niestety nie da się ich zwiększyć do jakichś niebotycznych wartości – VS broni jakichś swoich (prawdopodobnie – wewnętrznych – maksimów), ale trochę możemy je ponaginać. Wystarczy odnaleźć tylko interesującą nas zmienną Timeout :).