Diverse prosjekter

Her kan du lese om diverse utviklingsprosjekter jeg driver med – hobbyprosjekter stort sett, inkludert open source prosjekter jeg har startet eller bidrar på.

Ping Ring del 3: Ruby

Dette er del 2 i artikkelserien Ping Ring hvor jeg implementerer et og samme program i et utall ulike programmeringsspråk – for å se om det er noe å lære gjennom å gjøre det. Introduksjonen kan du lese her.

Neste språk ut er Ruby. Denne implementasjonen er logisk sett identisk med C#-varianten fra del 2. TCP-logikken er ørlite enklere (jeg trenger ingen StreamReader/Writer), og Ruby’s kodeblokker gjør tråd-logikken mere elegant å sette opp enn med C#’s lambda-uttrykk pluss et kall til start(). Men i bunn og grunn er det liten forskjell i hvor enkelt det er å løse oppgaven.

Hovedforskjellen er syntaksen. Ruby’s dynamiske typing gjør at vi unngår endel av hva jeg vil kalle støy. Syntaksen føles mere “komprimert” i Ruby, og det ser man igjen i bl.a. færre linjer kode. Min Ruby-variant “klokker seg inn” på 59 linjer – mot C# sine 90.

1 require socket
2
3 class RingServer
4   def initialize
5     @self_port = ARGV.shift
6     @other_port = ARGV.shift
7     @max_delay = ARGV.shift.to_i
8     @should_send_startup_ping = ARGV.shift == true
9     @last_ping_time = Time.now
10     puts ** Ruby Ring Server (#{@self_port})
11   end
12   def start
13     send_delayed_ping if @should_send_startup_ping
14     start_missing_ping_alert_thread
15     start_ping_listener_thread
16     Thread.list.each { |t| t.join unless t == Thread.main }
17   end
18   def send_delayed_ping
19     Thread.new do
20       sleep 1
21       begin
22         client = TCPSocket.new(127.0.0.1, @other_port)
23         client.print Ping from #{@self_port}
24       rescue
25         puts *** Failed sending ping: #{$!}
26       else
27         client.close
28       end     
29     end
30   end
31   def start_missing_ping_alert_thread
32     Thread.new do
33       while true
34         sleep 5
35         ping_delay = Time.now – @last_ping_time
36         if ping_delay > @max_delay
37           puts *** ALERT, RING BROKEN! No ping in #{ping_delay} seconds.
38           send_delayed_ping # try to wake up ring
39         end
40       end 
41     end
42   end
43   def start_ping_listener_thread
44     Thread.new do
45       listener = TCPServer.new(127.0.0.1, @self_port)
46       while session = listener.accept
47         process_incoming_ping session.gets
48         session.close
49       end
50     end
51   end
52   def process_incoming_ping message
53     @last_ping_time = Time.now
54     puts Received #{message}
55     send_delayed_ping
56   end
57 end
58
59 RingServer.new.start

Det vil være riktig av deg å påpeke at jeg ikke bruker blanke linjer på samme måte i denne koden som jeg gjorde i C#, og at det derfor er naturlig at det blir færre linjer. Men ta en titt nedenfor, hvor jeg har satt opp de to implementasjonene side om side. Her har Ruby-varianten fått seg endel linjeskift, slik at metodene som gjør det samme starter på samme linjenummer.

pingring_csharp_vs_ruby

Det er ikke tvil om hvilken versjon jeg synes er mest elegant. Men elegansen ligger altså i at C#-syntaksen har for mye støy og staffasje, ikke at det var spesielt mye enklere å implemetere i Ruby.

Jeg må derimot få påpeke at Ruby gir meg større fleksibilitet i hvordan jeg ønsker å organisere koden. Jeg valgte å bruke den samme strukturen som C#-varianten denne gangen; én klasse med seks metoder som løste helt konkrete behov. Jeg kunne derimot for eksempel ha valgt en klasse-løs implementasjon – uten å miste noe lesbarhet av betydning. Ruby egner seg etter mening veldig godt til “små skripts” som dette, hvor kravet til “innpakning” (encapsulation) ikke er så stort.

Alt i alt: Ikke en veldig stor forskjell, men Ruby trekker det lengste strådet! I neste del vil du få se programmet i et språk som har enda finere og mere kompakt syntaks, så følg med..

Kildekoden fra denne blogposten er tilgjengelig på Github. Der står du fritt til å forgrene løsningen og gjøre egne modifikasjoner om du ønsker det (for å illustrere et poeng eller lignende). Som alt annet på bloggen er koden lisensiert under Creative Commons.

Ping Ring del 2: C#

Dette er del 2 i artikkelserien Ping Ring hvor jeg implementerer et og samme program i et utall ulike programmeringsspråk – for å se om det er noe å lære gjennom å gjøre det. Introduksjonen kan du lese her.

Det første språket jeg har valgt å implementere PingRing-programmet i er det jeg kjenner aller best, nemlig C#. Først får du kildekoden til programmet i sin helhet. Deretter følger en nærmere forklaring av hva jeg har gjort, og til slutt en litt mere kritisk vurdering av designet. Trenger du å friske opp hukommelsen i forhold til spesifikasjonen kan du jo skumlese del 1 en gang til.

1 using System;
2 using System.IO;
3 using System.Net;
4 using System.Net.Sockets;
5 using System.Threading;
6
7 public class RingServer
8 {
9     public static void Main(string[] args)
10     {
11         Console.WriteLine(“** C# Ring Server ({0})”, args[0]);
12         new RingServer // Parse arguments and start server..
13         {
14             ThisPort = int.Parse(args[0]),
15             OtherPort = int.Parse(args[1]),
16             MaxDelay = new TimeSpan(0, 0, int.Parse(args[2]))
17         }.Start(bool.Parse(args[3]));
18     }
19
20     private DateTime _lastPingTime = DateTime.Now;
21     public TimeSpan MaxDelay { get; set; }
22     public int OtherPort { get; set; }
23     public int ThisPort { get; set; }
24
25     public void Start(bool sendStartupPing)
26     {
27         if (sendStartupPing)
28             SendDelayedPing();
29         StartMissingPingAlertThread();
30         StartPingListener().Join();
31     }
32
33     private void SendDelayedPing()
34     {
35         new Thread(() =>
36         {
37             try
38             {
39                 Thread.Sleep(1000);
40                 using (var tcpClient = new TcpClient(“127.0.0.1″, OtherPort))
41                 using (var streamWriter = new StreamWriter(tcpClient.GetStream()))
42                     streamWriter.Write(“PING from “ + ThisPort);
43             }
44             catch (Exception ex)
45             {
46                 Console.WriteLine(“*** Failed sending ping: {0}”, ex.Message);
47             }
48         }).Start();
49     }
50
51     private void StartMissingPingAlertThread()
52     {
53         new Thread(() =>
54         {
55             while (true)
56             {
57                 Thread.Sleep(5000);
58                 var pingDelay = DateTime.Now – _lastPingTime;
59                 if (pingDelay > MaxDelay)
60                 {
61                     Console.WriteLine(“*** ALERT, RING BROKEN! No ping in {0} seconds.”,
62                         pingDelay.TotalSeconds);
63                     SendDelayedPing(); // try to wake up ring
64                 }
65             }
66         }).Start();
67     }
68
69     private Thread StartPingListener()
70     {
71         var listenerThread = new Thread(() =>
72         {
73             var tcpListener = new TcpListener(IPAddress.Parse(“127.0.0.1″), ThisPort);
74             tcpListener.Start();
75             while (true)
76                 using (var connection = tcpListener.AcceptTcpClient())
77                 using (var streamReader = new StreamReader(connection.GetStream()))
78                     ProcessIncomingPing(streamReader.ReadToEnd());
79         });
80         listenerThread.Start();
81         return listenerThread;
82     }
83
84     private void ProcessIncomingPing(string message)
85     {
86         _lastPingTime = DateTime.Now;
87         Console.WriteLine(“Received {0}”, message);
88         SendDelayedPing();
89     }
90 }

Programmet du nettopp har sett kan illustreres med diagrammet nedenfor. To tråder startes opp, én for å ta imot innkommende pings, og én for å sjekke at det har kommet en ping innen tidsfristen. De to trådene kommuniserer gjennom at lytte-tråden oppdaterer feltet _lastPingTime. Når det skal sendes en ping ut til neste server opprettes det en ny tråd for dette.

pingring_alg_1For å starte jobber i egne tråder bruker jeg Thread-klassen sin konstruktør som kan ta imot en lambda-funksjon, for så å kalle Start() på tråden. Lambda’en for lytteren og alerteren inneholder uendelige løkker (while true). I RingServer.Start() kaller jeg til slutt Join() på en av trådene slik at programmet blir stående og vente på at tråden skal bli ferdig, noen den selvfølgelig aldri gjør.

Det hadde kanskje vært mer naturlig å implementere StartMissingPingAlertThread med en Timer, men jeg valgte å holde meg til en felles måte å håndtere samtidigheten på.

Resten av programmet bør være nokså enkelt å forstå, og jeg går ikke mer i dybden nå. Jeg vil derimot komme tilbake til noen detaljer når jeg i senere blogposter kan sammenligne denne løsningen med implementasjoner i andre språk.

Er dette et bra design?

Vel, det kommer kanskje an på øyet som ser. Designet er i alle fall ikke drevet frem av tester, det er tydelig. For en mere objektorientert løsning burde jeg kanskje ha skilt ut de ulike ansvarene til egne klasser – en sender, en listener, en alerter – RingServer-klassen gjør nå alt for mye. Dessuten burde jeg kanskje laget abstraksjoner rundt TcpListener og TcpClient, slik at jeg kunne erstattet dem med falske implementasjoner under testing.

Men enkelhet er også en viktig egenskap, og selv om programmet i sin nåværende form ikke er enkelt å lage enhetstester for, så er det veldig oversiktelig. 90 linjer C# klarer man stort sett å wrappe hjernen rundt. Den funksjonelle kompleksiteten i programmet er altså ikke stor nok til å rettferdigjøre et mer fleksibelt design (slik jeg har vurdert det). Jeg kan heller ikke forutse fremtidige endringer som jeg bør ta høyde for – det koster mindre å starte fra scratch om det kommer endringer som ikke passer inn.

Dessuten egner det nåværende formatet seg bedre for denne artikkelserien, og til å sammenligne med andre implementasjoner, enn en mer kompleks objektstruktur ville gjort. Men det er altså verdt å merke seg at jeg bryter med endel SOLID-prinsipper med dette designet.

Kildekoden fra denne blogposten er tilgjengelig på Github. Der står du fritt til å forgrene løsningen og gjøre egne modifikasjoner om du ønsker det (for å illustrere et poeng eller lignende). Som alt annet på bloggen er koden lisensiert under Creative Commons.

Ping Ring del 1: Introduksjon

Dette er starten på en artikkelserie jeg har kalt Ping Ring. Jeg skal lage et program for å overvåke at et sett med servere er i live, og gjennom en rekke blogposter vil du få se det samme programmet implementert i et utall programmeringsspråk: C#, Ruby, Clojure, Erlang med flere.

Målet er å studere og demonstrere ulikhetene mellom språkene. I hvilket språk er det enklest å implementere programmet? Hvilken implementasjon er lettest å forstå? Følg med over de neste par ukene, og du vil kanskje finne svaret.

Oppgaven: Overvåking av servere

Se for deg at du har en haug med servere som du ønsker å vite at er operative – at de kjører, og er tilgjengelige på nettverket. En mulig strategi er å installere et lite program på hver server som sende et lite ping i en ring, som i figuren nedenfor.

pingring1

Om en server faller ut, vil ringen bli brutt. Dette vil man kunne oppdage to steder: Programmet som forsøker å pinge serveren som er ute vil ikke kunne opprette kontakt, og serveren etter den som er ute i ringen vil slutte å motta pings. Disse serverne kan da trigge en alarm og fortelle driftsteamet hvilken server som er nede.

pingring2

Jeg har valgt denne oppgaven fordi den ikke er så veldig vanskelig å implementere, selv om jeg må bruke noen teknikker som ikke dukker opp så veldig ofte i vanlig applikasjonsutvikling. For å løse oppgaven vil jeg bruke tråding/samtidighet, og low-level socket/TCP-kommunikasjon. Dette vil kunne vise interessante ulikheter i de ulike språkene. Programmet krever derimot ingen komplekse algoritmer eller data-hierarkier, som ville kunne gjøre det spesielt fordelaktig å bruke en bestemt klasse programmeringsspråk (som OOP vs. FP).

Detaljert spesifikasjon

Først noen begrensninger: Jeg kommer bare til å simulere at jeg har mange servere, så alle vil kjøre på localhost. Hver instans vil dermed bli identifisert med en port, og kan anta at all komunikasjon går på IP 127.0.0.1. Programmet skal kjøres som en konsoll-applikasjon, og tar fire parametre: (1) egen port, (2) porten til den neste serveren i ringen, (3) hvor mange sekunder det kan gå uten en ping før alarmen skal aktiveres, og (4) en indikator for om programmet skal starte med en ping (én av serverne må starte opp ringen).

Og for enkelhets skyld vil det å aktivere alarmen bare være å skrive en melding til konsoll-vinduet.

Programmet vil bestå av følgende, funksjonelle deler:

design Ping-sending:

Programmet må kunne sende en ping til en bestemt port (den neste serveren). Et ping består av teksten “Ping from X”, hvor X er porten det sendes fra. Sendingen skal skje fra en egen tråd, slik at det ikke påvirker programmets øvrige oppgaver. Programmet skal alltid vente ett sekund før ping sendes – vi må ikke overbelaste nettverket/serverne med pings.

Hvis den neste serveren i rekken er nede skal pingen feile, og programmet skal da melde fra om dette til konsollet.

Ping-mottak:

Programmet skal lytte på sin egen port etter pings. Pings skal mottas og behandles i en egen tråd, slik at det ikke påvirker programmets øvrige oppgaver. Når en ping mottas skal meldingen skrives til konsollet, og en ping skal sendes videre til neste server.

Ping-overvåking:

Programmet skal ha en egen tråd som i et bestemt intervall, for eksempel hvert femte sekund, kontrollerer at serveren mottar pings. Hvor mange sekunder serveren kan stå uten å motta pings er ett av oppstartsargumentene. Hvis denne grensen overskrides skal programmet skrive en alarm til konsollet som sier hvor mange sekunder det har gått siden siste ping ble mottatt. Det skal også sendes en ny ping for å forsøke å vekke ringen til live igjen.

Oppstart:

Under oppstart skal programmet lese inn argumentene og starte opp de nødvendige trådene. Det skal også identifisere seg ved å skrive ut sin egen port til konsoll-vinduet. Og hvis det siste argumentet er “true” skal det sendes en initiell ping for å starte opp ringen.

Og hva kommer nå?

Som jeg sa i starten skal jeg implementere dette programmet i en rekke, ulike programmeringsspråk. Vi kan forestille oss at vi har en overivrig systemansvarlig som nekter oss å kjøre annet enn C#-kode på C#-serveren, Ruby-kode på Ruby-serveren osv., og at vi derfor MÅ implementere mange varianter :P

Jeg kommer til å poste artiklene i rask rekkefølge, så følg nøye med i en uke eller to fremover.

Hvis du har lyst til å forsøke deg på en implementasjon selv kan det være lurt å gjøre det før du ser noen av mine implementasjoner.., så kan du i stedet sammenligne etterpå. I så fall er det kult om du gjør implementasjonen din tilgjengelig på pastie, CodeTidy, mysticpaste, Gist eller en annen pastebin, og deler linken med oss andre i en kommentar.

Pixel-liv

I denne blogposten om hvordan det er å være en gammer programmerer refererte Uncle Bob Martin i kommentarfeltet til noe han kalte Langton’s Ant. Wikipedia kunne fortelle meg at dette er en todimensjonal Turingmaskin oppfunnet av Chris Langton i 1986. Den består av et par enkle regler, men utviser kompleks, emergent adferd. Reglene er: Står mauren på en hvit celle, snu 90 grader til høyre – står mauren på en svart celle, snu 90 grader til venstre. Endre deretter fargen på cellen, og gå én celle frem.

Langton’s Ant virket som en interessant, liten programmeringsoppgave i samme bane som Conway’s Game of Life. Jeg fikk selvsagt lyst til å prøve meg på en implementasjon, så jeg satte igang i mitt favoritt-fritids-språk Ruby. For den grafiske representasjonen fant jeg frem Gosu, et genialt og enkelt rammeverkt jeg har sett litt på tidligere for å lage grafiske spill i Ruby. Gosu gir meg et vindu med en update-draw-syklus (for dem som er vandt til spill-utvikling), og enkle metoder for å plassere og manipulere grafikk.

Og her er resultatet – etter noen få hundre steg har mauren laget en liten krussedull:

Ser du den lille, rød mauren? Etter noen tusen steg virker det som om alt bare er kaos:

Men etter rundt 10.000 steg begynner det å skje noe. Mauren begynner å lage en “motorvei”. Det er ikke bevist at dette alltid vil skje, men alle forsøk som er gjort – uansett start-forhold – har før eller senere produsert et slikt mønster.

Det var ganske kult å for en gangs skyld ikke kunne skrive enhetstester for adferden jeg var ute etter. I stedet måtte jeg vente over ti tusen sykler (ca 2,8 minutter – 0,01685 sekund pr steg – en framerate på nesten 60 pr sekund) for å se at mauren min fungerte som den skulle. Jeg kunne selvsagt skrevet tester for andre ting, men valgte å kjøre uten sikkerhetsnett denne gangen.

Hvis noen er interessert i å se koden så ligger den på min github-konto. Selve logikken for å vise et todimensjonalt sort/hvitt grid i et Gosu-vindu gjorde jeg generell. Jeg implementerte også en versjon av Conway’s Life med den – den ble noe tregere enn jeg hadde håpet, skylder det på Gosu-rammeverket inntil jeg finner en bedre optimalisering i min egen kode :P

Hva har du gjort i dag for å bli en bedre utvikler enn du var i går? Her er to forslag til hva du kan gjøre. 1) Les koden min, last den ned, og forsøk enkelte modifikasjoner for å forsikre deg om at du forstår den. Kom gjerne med spørsmål til koden, eller forbedringsforslag. Eller 2) Implementer din egen Langton’s Ant. Hva med en .NET-implementasjon vha. XNA?

QuickBencher (beta)

I går lanserte jeg versjon 0.1 av et lite open source bibliotek jeg har valgt å kalle QuickBencher. Dere finner det på quickbencher.codeplex.com. Formålet er å gjøre det litt enklere å måle hvor lang tid ting tar, og kjøre sammenligninger på ulike større eller mindre kodesnutter. Her er et eksempel:

    1 var list = new List<string>();

    2 for (int i = 0; i < 10000000; i++)

    3     list.Add(i.ToString());

    4 

    5 string dummy;

    6 Benchmark.Bm(x =>

    7 {

    8     x.Report(“For:”, ()

    9         => { for (int i = 0; i < list.Count; i++) dummy = list[i]; });

   10     x.Report(“While:”, ()

   11         => { var enu = list.GetEnumerator(); while(enu.MoveNext()) dummy = enu.Current; });

   12     x.Report(“Foreach:”, ()

   13         => { foreach (var item in list) dummy = item; });

   14     x.Report(“Lambda:”, ()

   15         => { list.ForEach(s => dummy = s); });

   16 });

Output:

                  user     system      total        real
  For:        0.140401   0.000000   0.140401 (  0.147000)
  While:      0.124801   0.000000   0.124801 (  0.122000)
  Foreach:    0.124801   0.000000   0.124801 (  0.121000)
  Lambda:     0.062400   0.000000   0.062400 (  0.093000)

Mens jeg holdt på å lære meg Ruby kom jeg over en modul som heter Benchmark. “En sånn modul skulle jeg hatt i verktøyskrinet mitt når jeg utvikler i C#,” tenkte jeg, og siden det ikke så ut som om noen hadde gjort det før så lagde jeg den.

Ta en titt på prosjektsiden på CodePlex for mer info og for å laste ned v0.1. Den skal fungere mer eller mindre som tiltenkt, men det hadde vært kjekt å få litt tilbakemelding på om den fungerer for andre enn meg. Jeg tar gladelig imot forbedringsforslag, og hvis noen har lyst til å bidra på andre måter så er dere velkomne til det.

Nytt prosjekt: SharpBrain

Jeg begynte et nytt hobbyprosjekt i helgen – et 2D brettspill i WPF jeg har valgt å kalle SharpBrain, eller #Brain om du vil. Denne gangen tror jeg at jeg har et brukbart konsept, og at det vil være noe jeg kan fullføre. Her er et foreløpig screenshot – en del av grunnlogikken ble ganske raskt klar, men den eneste visuelle sålangt er selve spillbrettet:

CropperCapture[39]

SharpBrain vil i utgangspunktet bli et ganske enkelt spill mellom to eller flere “styrker” plassert på et hexagonalt rutenett. Styrken din vokser for hver runde, og man kan spre styrkene ved å bevege seg rundt og angripe/konsumere andre styrker. Det mest interessante i Sharpbrain vil derimot bestå av diverse “pluggable” moduler:

– For det første vil spillet vil ha et utvidbart sett med game actions – handlinger spilleren kan utføre når det er hans tur. Den grunnleggende handlinger er move, men vil bli utvidet med diverse andre, som teleport, shoot, shield, explode, etc. De ulike handlingene kan ha ulike conditions som gjør at de blir tilgjengelige.

– Sharpbrain vil også kunne endre karakter ganske drastisk gjennom et sett med game rules. Tanken er at dette skal være moduler som kjøres for hver celle etter hver spillers tur, og kan gi spilleren ulike fordeler eller ulemper basert på spillets gang, hvor mange styrker spilleren har i den bestemte cellen o.l.

– Videre vil jeg implementere ulike game judges, som avgjør hvordan man vinner spillet. Et scenario vil for eksempel være at man skal utslette alle andre styrker. Et annet kan være at man skal okkupere en eller flere predefinerte celler i en eller flere runder.., et slags “capture and hold” modus.

– Til slutt vil jeg implementere ulike AI’er, sånn at man kan spille mot maskinen, som kan benytte ulike strategier for å forsøke å slå deg. Jeg tror dette kan bli den mest utfordrende delen av utviklingen – selv om spillet i utgangspunktet er ganske enkelt, så åpner det opp for ekstremt mange muligheter i løpet av spillets gang – ikke minst når man legger til flere actions, rules og judges. Drømmen er at folk får lyst til å bidra med utvidelser når jeg er ferdig med grunnimplementasjonen av spillet. Jeg skal i alle fall sørge for å gjøre det relativt enkelt å legge til ny plug-ins. Dessuten vet jeg at det er mange som har sansen for å lage AI’s, og dette vil kunne bli en brukbar plattform for å kjøre “robot battles” mellom dem. Kanskje kunne jeg lagt opp muligheten for å lage AI’s i IronRuby og IronPython – det hadde vært konge!

Følg med fremover på hvordan dette (forhåpentligvis) utvikler seg..

"Vil du bli en .NET Ninja" lansert

I helgen lanserte jeg endelig første release av DotNetNinjaQuiz, min versjon av Vil Du Bli Millionær for .Net-utviklere. Dette er et prosjekt jeg begynte på i januar i år (blogget om det her) som et første forsøk på å lage en WPF applikasjon. Jeg tok opp tråden nå for å kunne spille spillet på høst-kickoff’en til utviklerne i PSWinCom.

Version1_level1

Jeg har gitt DotNetNinjaQuiz et hjem på google code. Etter at jeg la ut versjon 1 twittet jeg om det, og responsen var bra, for etter bare noen få timer hadde nesten 40 personer lastet det ned, og nå – fire dager senere – har over 80 besvart .NET-spørsmål. Det er gøy å lage ting når folk er interessert i å prøve det ut.

Som sagt kodet jeg det meste av dette programmet i januar – i starten på .Net Ninja-initiativet mitt – og det bærer koden preg av. Jeg hadde praktisk talt ikke gjort noe i Windows Presentation Foundation tidligere, og jeg behersket hverken testdreven utvikling eller presentation patterns på den måten jeg gjør i dag. Men gjennom prosjektet fikk jeg prøvd meg på litt av hvert, som for eksempel dynamisk skalering av elementene på skjermen, lasting av grafikk og avspilling av lydeffekter. Jeg brukte også db4objects for persistering, en teknologi jeg akkurat hadde oppdaget på den tiden.

Hvis du ikke har prøvd spillet enda så håper jeg du vil laste det ned og ta en titt. Hva med å for eksempel kjøre et lite “game show” på jobben? Eller foreslå å spille det på neste brukergruppemøte?!

Jeg er forresten veldig interessert i bidrag til neste versjon – spesielt i form av flere spørsmål – så ta gjerne kontakt om du er interessert.

Fussball Manager blir Åpen Kildekode

På grunn av flere etterspørsler har jeg gjort Fussball Manager programmet jeg lagde for et par år siden tilgjengelig som et open source prosjekt på google code. Adressen er code.google.com/p/foosballmanager. Så hvis du trenger et program til å holde oversikt over hvem som er kontorets beste foosballspiller så vet du nå hvor du skal finne akkurat det. Programmet kan egne seg godt også for andre spill, sålenge du kun skal tracke seier og tap, og ikke er interessert i hvor stor hver seier er.

Samtidig migrerte jeg løsningen til Visual Studio 2008 / asp.net 3.5, og endret den fra å være en Web Site til å bli en Web Application. Er du interessert i å hjelpe meg å vedlikeholde og utvide programmet så er du velkommen til å delta.

Tidligere poster om Fussball Manager: Fussball Manager lanseres | Fussball Manager får statistikk | Fussball Manager versjon 2.

Snookiepoof på google code

Snookiepoof er et lite C# prosjekt jeg har jobbet med de siste ukene, hvor jeg eksperimenterer med ting som OO-design patterns og clean code. Jeg planlegger å skrive endel blogposter fremover basert på erfaringer med dette prosjektet.

Snookiepoof er en motor for rollespill-aktige spill, hvor man beveger seg rundt i en verden og oppdager ting man kan gjøre. Foreløpig inneholder løsningen et konsoll-basert interface, men designet skal også kunne støtte andre typer brukergrensesnitt.

Jeg bruker TDD/BDD under utvikling, og legger fokus på gode abstraksjoner og å følge SOLID-prinsippene. Command-pattern er mye brukt, og spillmotoren inneholder bl.a. min implementasjon av en generisk state machine.

Av eksterne avhengigheter kan jeg nevne at jeg har brukt TinyBDD til å skrive de fleste testene, og bruker Ninject for dependency injection. Jeg planlegger også å bruke db4o for persistering.

Jeg har lagt ut prosjektet på Google Code, så hvis du er interessert i å ta en titt så har du muligheten til det.

Knagger:

Overvåk ledig diskplass med WMI

Med gjevne mellomrom går en eller annen disk på en eller annen server full, enten det er på grunn av ivrige logger, akkumulerende backups, voksende databaser eller noe helt annet. Uansett hva det er skaper det alltid problemer og tapt arbeidstid. Og med like gjevne mellomrom kan man høre meg klage, og si at nå vi få på plass en løsning som kan overvåke diskene våre.

I dag bestemte jeg meg derimot for å ikke klage lengre, og 20 minutter senere hadde jeg laget en liten app som viste meg hvor mye ledig diskplass det var på serverne våre. Jeg ante ikke at det skulle være enkelt.

Jeg begynte med å lage en liten dataklasse for å holde på informasjonen om en disk. Med hjelp av CodeRush tok det ikke mange sekunder eller tastetrykk. Her er DiskInfo-klassen:

    8 public class DiskInfo

    9 {

   10     public string DriveLetter { get; set; }

   11     public string VolumeName { get; set; }

   12     public double Size { get; set; }

   13     public double Free { get; set; }

   14     public double PercentFree { get { return Free / Size; } }

   15 

   16     public override string ToString()

   17     {

   18         return string.Format(“{0}{1}{2}{3}{4}”,

   19             DriveLetter.PadRight(5),

   20             VolumeName.PadRight(30),

   21             Size.ToString(“###,###,###,###”).PadLeft(15),

   22             Free.ToString(“###,###,###,###”).PadLeft(15),

   23             PercentFree.ToString(“#0%”).PadLeft(10));

   24     }

   25 }

Og så trengte jeg noe som kunne hente ut informasjonen. Jeg har hørt mye om Windows Management Instrumentation (WMI), men aldri brukt det. Men en liten titt på System.Management namespacet avslørte at dette var akkurat det jeg behøvde:

Provides access to a rich set of management information and management events about the system, devices, and applications instrumented to the WMI infrastructure. Applications and services can query for interesting management information (such as how much free space is left on the disk, what is the current CPU utilization, which database a certain application is connected to, and much more)…

Jeg laget en kjapp liten command-klasse jeg valgte å kalle DiskInfoRetriever, som gitt navnet på en server henter ut informasjon om alle diskene på den maskinen:

    9 using System.Management;

   10 

   11 public class DiskInfoRetriever

   12 {

   13     private string _Server;

   14 

   15     public List<DiskInfo> Items { get; set; }

   16 

   17     public DiskInfoRetriever(string server)

   18     {

   19         _Server = FixServerName(server);

   20         Items = new List<DiskInfo>();

   21     }

   22 

   23     private string FixServerName(string server)

   24     {

   25         return server.StartsWith(@”\\”)

   26             ? server

   27             : @”\\” + server + @”\root\cimv2″;

   28     }

   29 

   30     public void Execute()

   31     {

   32         var scope = new ManagementScope(_Server);

   33         var query = new ObjectQuery(

   34               “select FreeSpace, VolumeName, Size, Name “

   35             + “from Win32_LogicalDisk where DriveType=3″);

   36 

   37         var searcher = new ManagementObjectSearcher(scope, query);

   38         var results = searcher.Get();

   39 

   40         Items.Clear();

   41 

   42         const int MEGABYTE = 1048576;

   43         foreach (ManagementObject result in results)

   44         {

   45             Items.Add(new DiskInfo

   46             {

   47                 DriveLetter = result["Name"].ToString(),

   48                 VolumeName = result["VolumeName"].ToString(),

   49                 Size = (double.Parse(result["Size"].ToString()) / MEGABYTE),

   50                 Free = (double.Parse(result["FreeSpace"].ToString()) / MEGABYTE),

   51             });

   52         }

   53     }

   54 }

Legg merke til den SQL-lignende spørringen på linje 34/35.

Nå gjenstod det bare å definere en liste med servere, initialisere en bunch med command-objekter, eksekvere dem, og loope gjennom resultatet for å skrive objektene til konsollet. Det gjorde jeg i Main:

   10 static void Main(string[] args)

   11 {

   12     string[] servers = { “s1dev02″, “s1dev03″, “s1dev04″, “s1dev05″, “s1hvsql01″ };

   13 

   14     try

   15     {

   16         foreach (var server in servers)

   17         {

   18             Console.WriteLine(server + “:”);

   19 

   20             var diskInfoRetriever = new DiskInfoRetriever(server);

   21             diskInfoRetriever.Execute();

   22             diskInfoRetriever.Items.ForEach((disk) => Console.WriteLine(disk));

   23 

   24             Console.WriteLine();

   25         }

   26     }

   27     catch (Exception ex)

   28     {

   29         Console.ForegroundColor = ConsoleColor.Red;

   30         Console.WriteLine(ex.ToString());

   31     }

   32 

   33     Console.WriteLine();

   34     Console.WriteLine(“press ENTER to exit..”);

   35     Console.ReadLine();

   36 }

20 minutter tidligere hadde jeg ingen anelse om at jeg kom til å lage dette her, eller at det var så enkelt. For fremtiden skal jeg tenke meg om en ekstra gang før jeg klager over en manglende, teknisk løsning. Det meste lar seg løse, og mye er enklere enn man tror.

Neste steg blir å lage en ny modul til ContikiCenter som kan informere oss om når disker nærmer seg smertegrensen. Her er forresten resultatet av dagens kjøring:

Disk Watcher console app

Obs, det ser ut som om vi må foreta en liten opprydning på s1dev04. Jaja, det får vente til i morgen.. :)


Einar W. Høst: Det er jo læringen som gjør det morsomt! Se også http://norvig.com/21-days...

Pagliacci: OBS! tl;wr. Det er vel akuratt det jeg sliter med med min læring innenfor pr...

Torbjørn: La oss anta to ulike definisjoner av Template Method pattern - de to ytterpunkte...

Lars-Petter: Hei igjen. Siden du inviterer til å ta diskusjonen i bloggen, og har tatt deg t...

Torbjørn: Lars-Petter: Det er én måte å se det på. Det er absolutt fortsatt Template M...

Lars-Petter: Hei. Har du ikke i prinsippet her gått over fra Template Method (arv) til Strat...

Christian Abildsø: I alle fall i C#, så føles dette som kode som blir mer fleksibel men vanskelig...

Torbjørn: Hei Henrik, og takk for tilbudet. Ble oppmerksom på Rasberry Pi for under en uk...

Henrik Sandaker Palm: Ang. større hobby prosjekt. Du er som er en slik rakker på programmering har j...

Øivind Nilsen: Slutt å bruke mobilnummeret mitt som eksempel !...

 Hold deg oppdatert

Søk i bloggen

Ferske innlegg

  • En historie om programmering
  • Template Method del 4: Multippel arv
  • Template Method Intermesso
  • Template Method del 3: Bare funksjoner
  • Kategorier

  • .net ninja (37)
  • Bøker (17)
  • Diverse prosjekter (35)
  • DSL (10)
  • Erlang (10)
  • F# (5)
  • Hardware (1)
  • Jobb (77)
  • Julekalender (51)
  • kjempekjekt.com (23)
  • LISP/Clojure (33)
  • NNUG / community (60)
  • O/RM & databaser (10)
  • Off topic (116)
  • OO-design/clean code (30)
  • Podcasts (14)
  • Polyglot (77)
  • Ruby (27)
  • Silverlight / RIA (3)
  • Software/verktøy (20)
  • Softwareutvikling (21)
  • Testing / TDD (30)
  • the contiki strip (13)
  • User experience (3)
  • WCF (3)
  • Webutvikling (32)
  • WPF (9)
  • WTF (12)
  • Last ned Wallpaper

    Programmeringsbloggens tøffe skrivebordsbakgrunn med snippets fra ulike språk laster du ned her!

    Abonner via epost

    Om du vil kan du få alle nye blogposter tilsendt til din epost. Abonner nå, det er kjempeenkelt!

    Meta