2016. január 31., vasárnap

Lean Poker workshop

Hétvégén részt vettem egy Lean Poker nevű eseményen, ami egy szakmai workshop volt az Emarsys CraftLab szervezésében. A Lean Poker alapvetően a lean startupból nőtte ki magát és mint olyan, itt is az számít, hogy ki mennyire képes felmérni a piaci helyzetet és reagálni a körülmények változására. Ebből adódóan a workshop célja,  hogy fejlessze ezen képességeinket. Az eseményről bővebb infó itt található.

A feladat 

Néhány fős csapatokba szerveződve készítettünk csapatonként egy-egy poker robotot, amik folyamatos házibajnokság keretében mérték össze tudásukat egymással szemben. A workshop felépítése kb. 1 órás fejlesztői rész, rövid össznépi élménybeszámoló és szünet hármasok egymásutánjából állt, egy beszélgetős ebédszünettel kibővítve. A beszámolók során minden csapat elmondhatta, hogy mivel küzdött és milyen játékstratégiával próbálkozott. Mivel nem volt tétje a "versenynek", így minden csapat nyugodtan megoszthatta tapasztalatait.

Continuous Integration and Deployment

A workshop során a Continuous Integration and Deploymentbe kóstolhattunk bele, aminek az a lényege, hogy minden változtatás azonnal ki is megy éles környezetbe, feltéve, hogy fordul a kód. Alapvetően tetszik az elgondolás, hogy ezzel elvileg csak kis változtatások történnek, ráadásul maximum néhány órás nagyságrendeken belül meg is van a visszajelzés, hogy bevált-e a módosítás. Valamint elvileg ezzel együtt jár az is, hogy a fejlesztők ennek tudatában mindig csak átgondolt és jól működő változtatásokat csinálnak.

Véleményem szerint egy nagyon hasznos dolog a Continuous Integration System, vagyis egy olyan rendszer, ami minden változtatás esetén rávizsgál, hogy legalább fordul-e a kód. Viszont nem teljesen értek egyet a Continuous Deployment gyakorlati használhatóságával. Számos helyen, például egy 24 órában működő diszpécseri szolgálatnál, ahol jelentős az automatizált működés, egy hibás változtatás sokáig rejtve maradhat, komoly anyagi károkat okozva.


Összefoglalás

Az esemény egyaránt érdekes volt számomra mind emberi, mind szakmai szemszögből nézve. Szeretek gondolkodós feladatokat megoldani, rendszereket tervezni, amit itt aktívan kiélhettem. Megfelelő arányban vették komolyan és lazán a csapattársaim az egész "versenyt", olyan volt a légkör, mintha csak néhány barát ült volna össze egy könnyed kódolásra iszogatás közben. 

A csapatunkban éppúgy volt rutinos programozó, mint IT-n kívülről érkezett is. A kezdeti káoszt tovább erősítette az a tény is, hogy szinte mindenki ismeretlenül került össze. Idővel leküzdöttük a káoszt, ami nagyban elősegítette a hatékony munkaszervezést. Ezt követően érezhetően jobban tudtunk együttműködni, ami meglátszott az eredményünkön is: már "csak" stagnált az arányunk a relatív eredménytáblában :)

Összességében jelentős tanulság volt számomra, hogy mennyire fontos az emberi kapcsolat a csapaton belül. Nem elhanyagolhatók egy projekt kapcsán az egyének szakmai kompetenciái, de ennél is fontosabb, hogy tisztában legyünk azzal, kivel milyen formában lehet és kell kommunikálni. Tudni kell, ki mennyire és milyen jellegű feladattal terhelhető, hogy hatékonyan lehessen megosztani a munkát. Igenis kell erre időt szánni a munka elején, főként, ha ismeretlenek a társak tapasztalatai. Másrészről ismét előjött, hogy mennyire lényeges, hogy szánjunk időt a tesztekre, kifejezetten dinamikus nyelvek esetében.


A szervezéssel teljesen meg voltam elégedve, mivel korrekten fel volt építve, hogy mi a menetrend aznapra és szakszerűen le is vezették a terveknek megfelelően. Tetszik a kezdeményezés, mivel azon túlmenően, hogy jó móka, tanít is.

2016. január 14., csütörtök

Windows timeserver hangolása

Windows rendszeróra automatikus szinkronizálásakor alapértelmezetten a time.windows.com időkiszolgáló van beállítva 7 napos frissítési periódussal. A szinkronizáláshoz az UDP 123 port nem lehet letiltva, mivel ezen keresztül kommunikál.

Time server beállítása

Ahhoz, hogy működhessen a szinkronizálás, előbb be kell állítani egy központi rendszert, egy time szervert, amitől kéri el a pontos időt. Ezt a Dátum és idő beállításoknál lehet megadni:


Célszerű lecserélni az alapértelmezett time.windows.com kiszolgálót, mert megbízhatatlan, ahogy már régebben is írták róla, például itt vagy itt, hogy nem ajánlják a használatát. Ezt a napokban magam is tapasztaltam Windows Server 2012-n, így meg tudom erősíteni, hogy ma is az, ezért helyette a time.nist.gov-ot használom, eddig problémamentesen.


Frissítési periódus módosítása

Miután sikeresen be lett állítva, hogy milyen időkiszolgálót használjon, ajánlott a frissítés gyakoriságát is megemelni, mert a heti egyszeri szinkronizálás az majdnem annyit ér, mintha emberöltőnként lenne, ennyi idő alatt komoly eltérés keletkezhet több rendszer ideje között.

Szerencsére van több mód is arra, hogy megváltoztathassuk a periódust. Egyik lehetőség, amit a Microsoft ír, hogy egy registry kulcsot módosítunk a kívánt másodpercekkel. Egy másik, ami szerintem szebb, hogy beütemezünk egy feladatot a kívánt gyakorisággal.


  • Registry módosítása
Az alábbi kulcsot kell módosítani a registry-ben: 
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\
services\W32Time\TimeProviders\NtpClient

Ezen belül a SpecialPollInterval tartalmazza másodpercben mérve, hogy mennyi idő elteltével kell újra frissíteni a rendszerórát.

  • Ütemezett feladat
Az előbbinél szerintem sokkal szebb megoldás, hogy ha készítünk egy bat fájlt, amit az Ütemezőnek odaadunk, hogy a kívánt időközönként futtassa meg. A beütemezett fájl tartalma az alábbi legyen:

net start w32time
w32tm /resync

Működését tekintve elindítja a Windows Time szolgáltatást, ha esetleg még nem futna, majd utána kiadja a szinkronizálás parancsot. Ütemezett feladat létrehozásáról bővebben itt lehet olvasni. Ezen módszer mellett eléggé meggyőző érv, hogy az Ütemezőnek köszönhetően egy barátságos grafikus felületen keresztül lehet megadni a gyakoriságot, szemben a registry-vel, amiben eleve nem szívesen módosít az ember.

2016. január 13., szerda

Beragadt Windows RDP session

A napokban megesett velem, hogy egy Windows Server 2012-es gépre próbáltam bejelentkezni távoli asztallal (RDP) és a megnyíló ablakban folyamatosan azt jelezte, hogy épp próbál kijelentkezni. Néhány perces várakozás utána megszakítottam a kapcsolatot, majd pár órával később ismét megpróbáltam, ekkor vált már elkerülhetetlenné. Továbbra is a kijelentkezés képernyő fogadott.

Az MSDN-en rátaláltam két hasznos alkalmazásra. Az egyik a Qwinsta, ami az RDP sessionökról, munkamenetekről mond információt, a másik pedig a Rwinsta, aminek a segítségével törölni lehet adott RDP sessionöket.

Ezek segítségével sikerült megfelelően kijelentkeztetnem a beragadt felhasználómat és megszakítanom a munkamenetet.

Első lépésként egy másik felhasználó nevében, egész pontosan Adminisztrátorként sikeresen beléptem a távoli gépre, és qwinsta paranccsal kilistáztam a munkameneteket. Ezt követően a rwinsta "sessionname" utasítással lezártam a megfelelőt. Ezután már gond nélkül be tudtam jelentkezni ismét.


Azóta rátaláltam egy másfajta megközelítésre, ami szintén megoldás lehetett volna a problémámra: a távoli asztal a /console vagy /admin kapcsolóval történő indítása. A választás attól függ, hogy Windows Server 2008, vagy annál újabb rendszerről van-e szó, mivel a WS 2008-as idejében történt változás.  Ez a változtatás azt hozta magával, hogy lényegében a /console érvénytelenítve lett, nincs már rá szükség. A kapcsolókról és magáról a változtatásról itt lehet olvasni. 

A problémámhoz kapcsolódóan az volt a lényeges, hogy ezzel a módszerrel mindig be lehet kapcsolódni a session0 munkamenethez, vagyis olyan, vagy legalábbis nagyon hasonló hozzáférést ad a szerveren a konzolhoz, mintha nem is RDP-n keresztül léptem volna be, hanem fizikailag ténylegesen az adott gépről futtatnám. A módszer hátránya, hogy megszakítja annak a munkamenetét, aki szintén ilyen módon volt becsatlakozva.

2016. január 12., kedd

Unity3D és az egyszerű vonalrajzoltatás

Mostanság szembesültem Unity3D-ben olyannal, hogy az egyébként egyszerűnek tűnő feladat, miszerint néhány vonalat kellene kirajzolni, nem is olyan triviális. Néhány, a felhasználó által megadott pont által határolt területet akartam körberajzolni. Összefoglalom a három megvizsgált módszerrel kapcsolatos tapasztalataim.


Első közelítésben kézen fekvőnek tűnt, hogy a LineRenderert válasszam. Komoly korlátja, hogy csak folytonos vonalat tud kezelni, így ha külön lévő vonalakra van szükség, akkor ahhoz annyi LineRenderer példányra van szükség.

Másik negatívuma, bizonyos csillagállás mellett, megváltozik a vonalvastagság, eltorzulnak a vonalak. Ez a jelenség a LineRenderer működéséből adódik: nem 1 pixeles vonalat rajzol, hanem egy textúrázható, változtatható szélességű sávot (billboardot). Ebből adódik időnként az alábbi képen ábrázolt szituáció.

A LineRenderer a piros pontokat kapja meg kezdő- és végpontnak, a zöldeket pedig magának hozza létre a fentebb leírt működéshez. Előfordul azonban, hogy felcserélődnek a végpontnál lévő zöld pontok, ekkor torzul el a vonalszegmens. Ez különösen akkor jön elő, ha több szegmensből áll a vonal és erős az irányváltás az egyes szegmenseknél, mivel próbál optimalizálni és újrahasznosítja ezeket a zöld pontokat.


Magas szintű grafikai függvénykönyvtár arra, hogy közvetlenül lehessen mesh-t kirajzolni anélkül, hogy létre kellene hozni hozzá GameObjecteket és komponenseket. Ez utóbbi miatt gyorsabb a LineRenderernél, viszont alacsonyabb szintű a vezérlése, vagyis nekem kell ténylegesen létrehoznom a vonalakat. Körülményesebb, de nagyobb szabadságfokot biztosít. 

Maga a mesh felülethálóból épül fel, így egy vonal lényegében egy nagyon vékony négyszögként áll elő. Egy ilyen négyszög (quad) 4 vertexből áll, amihez nincs másra szükség csak a kezdő- és végpontokra illetve a vonal vastagságára.

Vector3 normal = Vector3.Cross(start, end);
Vector3 side = Vector3.Cross(normal, end-start);
side.Normalize();
Vector3 a = start + side * (lineWidth / 2);
Vector3 b = start + side * (lineWidth / -2);
Vector3 c = end + side * (lineWidth / 2);
Vector3 d = end + side * (lineWidth / -2);

Először meghatározzuk a normál-vektorát annak a síknak, amiben lesz a vonal. Ezután a side vektor a vonal oldalirányának a síkját jelöli ki, majd a normalizálással lesz belőle egységvektor. Végül ki kell számolni a tényleges vertexeket, amik megadják a quadot.

A felmerült problémára ez is teljes értékű megoldás lett volna, viszont indokolatlanul körülményes az implementáció a feladat mértékéhez képest.

GL osztály

Alacsony szintű és ezzel együtt közvetlen hozzáférést biztosít a rajzoló funkcionalitáshoz egy, az OpenGL-hez hasonló eszköztárral. Ez egyben az előnye és hátránya is, hiszen ez a leggyorsabb, ellenben kevesebb ráhatásom van, például nem állítható közvetlenül a vonalvastagság.

A GL osztálynál megemlítik a hivatalos oldalon, hogy az esetek nagyobb részében hatékonyabb a Graphics.DrawMesh használata, majd folytatják, hogy ezzel szemben sokkal körülményesebb a Graphics-ot kezelni a mesh miatt.

Ajánlott néhány dolgot észben tartani. Az OpenGL sajátossága miatt itt is normalizált viewporttal, képernyőtérrel kell számolni, azaz felbontástól függetlenül a képernyő bal alsó sarka mindig (0,0), míg a jobb felső (1,1) koordinátákkal címezhető. Továbbá bármennyire is adja magát, hogy tetszőleges shadert beállítsunk a GL használatakor a materialra, valamiért csak a honlapon írt "Hidden/Internal-Colored" beépített shader működött. Egyéb shadert kiválasztva a rajzoláshoz megadott színnek csak az alfáját volt hajlandó kezelni, az RGB értéket nem, így fixen fekete volt.

Összegezve

LineRenderert semmilyen körülmények között nem ajánlom, nemdeterminisztikus, mikor torzul el a vonal. Amennyiben a kényelmi funkcionalitás számít, akkor a Graphics, ha pedig a teljesítményen, a sebességen van a hangsúly, akkor egyértelműen a GL a jó választás szerintem.


Szerintetek van ezeknél kényelmesebb, hatékonyabb esetleg csak szebb megoldás arra, hogy kirajzolhassunk valamilyen egyszerű alakzatot Unity3D-ben?

2016. január 3., vasárnap

PDF vízjelezése és JPEG-be mentése

A minap találkoztam egy olyan feladattal, hogy .NET környezetben kellett PDF-et kezelnem, többek között már létezőt vízjeleznem, illetve JPEG-be kimentem. Kifejezetten csak ingyenes eszközöket akartam használni, így több jelöltet is megvizsgáltam, hogy melyik mennyire teljesítené az elvárásaimat.


DynamicPDF
Moduláris, bizonyos részei nem csak .NET-en érhetők el, hanem JAVA és COM komponensként is. Hátránya, hogy az ingyenes verzió igaz, hogy korlátlan ideig és teljes értékűen használható, de a saját logójukkal vízjelezi a PDF fájlokat.

GhostScript.NET
A GhostScript alkalmazás köré készült .NET wrapper, emiatt a tényleges PostScript utasításokat kell illetve lehet megadni számára, például egy vízjelként használandó felirat illesztéséhez. Mivel csak egy wrapper, így telepítve kell lennie a GhostScriptnek.

PDFSharp
Ingyenes, nyílt forráskódú, de nem lehetséges képként menteni az egyes oldalakat, pusztán csak a PDF-ben található képeket tudja kimenteni.

Spire.NET
Minden elvárt feladatra alkalmas, viszont az ingyenes verzió csak 10 oldalt kezel.

Először a fentiek közül a GhostScript.NETet választottam a PostScript utasítások ellenére is, mivel az ingyenesség nem járt számomra zavaró korlátokkal. Szerencsére megkerülhető, hogy telepítve legyen a GhostScript, mivel elegendő, ha közvetlenül a DLL van betöltve:

var gvi = new Ghostscript.NET.GhostscriptVersionInfo(
                  new Version(0, 0, 0), 
                  Path.Combine(path,"gsdll32.dll"), 
                  string.Empty, 
                  Ghostscript.NET.GhostscriptLicense.GPL);


Számos példaprojekt létezik hozzá a projekt GitHub oldalán, például arra, hogy miként lehet JPEG-be menteni az egyes oldalakat, vagy esetleg vízjelet hozzáadni. Sajnos az ékezetes betűk okozta karakterkódolási problémát nem sikerült megoldanom. Emiatt mégsem használtam vízjelezésre a GhostScript.NET-et.

Megoldás

Végül kombináltam a GhostScript.NET és a PDFSharp eszközöket. Utóbbival könnyedén lehet vízjelezni ékezetes betűkkel is a PDF fájlokat és képes MemoryStreambe is menteni, amit a GhostScript.NET-nek át lehetett adni paraméterként. Összességében egy MemoryStreamekkel operáló pipeline-t készítettem.



Ti ismertek a fentieken kívül olyan ingyenes eszközt PDF létrehozására, módosítására, amivel lehetséges képbe exportálni, és nincs benne olyan korlátozás, ami befolyásolná az elkészült fájl minőségét (nem kívánt vízjel, limitált oldalszám, stb)? Szerintetek miként lehet GhostScript.NET-ben PostScript utasítással hozzáfűzni Windows-on az ékezetes betűket egy létező PDF dokumentumhoz?

2016. január 2., szombat

CurrentCulture eltérő nyelvű rendszereken

Sokszor előjön az igény, hogy a kódban használjunk valami lokalizációhoz kötött logikát, ami lehet akár csak egyszerűen egy törtszám értelmezése is stringből vagy esetleg az, hogy egy adott karakterlánc mivel kezdődik. Az ilyen esetekben kézenfekvő választásnak tűnik a CultureInfo.CurrentCulture tulajdonság, hiszen pont erre való: területi beállításokat tartalmaz. 

Például Magyarországon egy átlagos magyar nyelvű Windows rendszeren ez tökéletesen működik tört számok értelmezésére:

float number = float.Parse("20,16", CultureInfo.CurrentCulture);
//number = 20,16

Viszont ez a kód hibásan működik az átlagos amerikai Windows verziókban, pontosabban minden olyan környezetben, ahol a területi beállítások között a program készítésekor tesztelt és elvárt formátumértékektől eltérőek vannak beállítva. Ha, mondjuk, valaki Magyarországon egy külföldi verziót használ gyári adatokkal, akkor a fenti kódban a tört számból kihagyja a tizedesvesszőt, mivel azt az ezres szeparátorának tekinti.

A CultureInfo.CurrentCulture az adott szálhoz tartozó CultureInfot adja vissza, ezért .NET 4-ben és az előtti verziókban minden esetben, amikor biztosra akarunk menni, szálanként meg kell adni, hogy milyen lokalizációt használjon. Szerencsére az újabb .NET-ben beállítható application domain szinten is, hogy mi legyen az alapértelmezett a CultureInfo.DefaultThreadCurrentCulture segítségével. Ezzel elegendő egyszer, az alkalmazás indulásakor beállítani a kívánt lokalizációt.


CultureInfo.DefaultThreadCurrentCulture = 
CultureInfo.CreateSpecificCulture("hu-HU");

...

float number = float.Parse("20,16", CultureInfo.CurrentCulture);
//number = 20,16

Programozót kódjáról

Annak ellenére, hogy szinte mindenki, aki rendszeresen dolgozott csapatban valamilyen szoftverprojekten, tudja, hogy minden programozónak van egy sajátos stílusa. Azt azonban kevesen tudják, hogy ezek a stílusjegyek jóval egyedibb mintát alkotnak, mint azt gondolnánk. Legalábbis amerikai kutatók erre az álláspontra jutottak a 2015. évben, akik szerint a programok az ujjlenyomathoz hasonlóan alkalmasak lehetnek a készítők azonosítására, akkor is, ha a csak a forráskód áll rendelkezésre.

A Marylandi Egyetem munkatársai előálltak egy olyan, gépi tanulási és nyelvelemzési algoritmusokat használó programmal, ami képes a forráskódok sajátosságainak kiszűrésére. A módszerük annyira hatékony, hogy a Google Code Jam programozási versenyének kódjai közül 95%-os pontossággal volt képes az egyes programok szerzőit megállapítani. Viszonyításképpen az ujjlenyomatokkal 97%-os pontosság érhető el a gyakorlatban.

Továbbfejlesztve a módszert, az újabb kutatási eredményük szerint nem szükséges a forráskód, mivel akár a lefordított kód is elegendő lehet a fejlesztő azonosítására. Ennek teszteléséhez a GitHubon elérhető programokat és a készítőkről megadott információkat használták fel a tanításhoz. A létrejött neurális háló kis számú fejlesztő esetén eléggé pontos: 96%-os, azonban a fejlesztők számát több százra növelve már csak az esetek felében adott helyes választ.

Az eljárásnak lehet létjogosultsága a gyakorlatban is: kézenfekvő megoldás lehet a rosszindulatú kódok létrehozóinak azonosítására, valamint segítséget nyújthat szerzői jogi viták esetén a valódi alkotó megállapítása.

CSV vessző vagy pontosvessző probléma

Előfordulhat, hogy Microsoft EXCEL-ben megnyitva egy CSV (Comma Separated Values) fájlt, az EXCEL nem találta meg, hogy hol is vannak a mezőhatárok, így pedig az első cellába tesz bele mindent. Ennek oka megegyezik azzal, amikor EXCEL-ből exportálunk CSV-be és nem pontosvessző lesz a szeparálókarakter.

Az alapvető probléma az, hogy az CSV-ben eredetileg a vessző (,) az elválasztó. Ehhez megfelelően az amerikai Windows verziókban a vessző az alapértelmezett Listaelválasztó (List Separator), ami a CSV-nek tökéletes. Ezzel szemben az európai verzióban a vessző a Tizedesjel (Decimal Symbol), és a Listaelválasztónak alapértelmezetten a pontosvessző (;) van megadva.

Az EXCEL-be való importáláshoz egy lehetséges megoldás, hogy ha CSV helyett TXT a kiterjesztés, mivel akkor megnyitáskor a megnyíló varázslóban kiválasztható, hogy mi a szeparátor.

Importálás és exportálás során egyaránt használható módszer (lehet, de nem ajánlott), ha lecseréljük a CSV fájlban az összes pontosvesszőt (;) egyszerű vesszőre (,).

Ennél sokkal hatékonyabb és célravezetőbb, hogy ha a Vezérlőpultban a területi beállításoknál módosítjuk az alapértelmezett értékeket.
  • Start | Vezérlőpult | Regional and Language Options
  • Additional Settings
  • Decimal Symbol legyen pont (.)
  • List Separator legyen vessző (,)
A beállításhoz tartozó ablakok

Ezt követően az EXCEL fel fogja ismerni, hogy ha pontosvesszővel vannak tagolva a CSV fájlban az értékek, valamint exportáláskor automatikusan pontosvesszővel fogja elválasztani az adatokat.