tag:aaron-fischer.net,2005:/feed
Aaron Fischer
2023-01-01T20:20:00+01:00
tag:aaron-fischer.net,2005:Article/287
2023-01-01T20:20:00+01:00
2023-01-01T20:20:00+01:00
DIY Network Attached Storage
<p>Wie baue ich mir mein eigenes NAS? Das ist mit Open Source und sorgfältig ausgewählten Komponenten nicht schwer. Ich habe mein altes NAS durch ein deutlich besseres ersetzt und dabei noch den Stromverbrauch reduziert.</p>
<h2 id="warum-das-ganze">Warum das ganze?</h2>
<p>Seit ich mit Computern zu tun habe (und das ist gefühlt schon mein ganzes Leben) ist es mein Anspruch, selbst Herr meiner Daten zu sein. Angefangen mit Diskettensammlungen hortete ich alles was mir heilig war. Darauf folgten wilde Basteleien mit Festplatten in Wechselrahmen oder auch mal freischwebend an einer <a href="https://de.wikipedia.org/wiki/NSLU2">NSLU2 von Linksys</a> (<em>natürlich</em> mit der <a href="https://web.archive.org/web/20120510051434/http://www.nslu2-linux.org/wiki/Unslung/HomePage">Unslug</a> Firmware). Seit ca. 10 Jahren verwende ich dafür nun ein <a href="https://n40l.fandom.com/wiki/Base_Hardware_N40L">HP Microserver (N40L Gen1)</a>, welcher mit 4 Platteneinschüben ausgestattet ist. Die Platten musste ich alle paar Jahre austauschen, doch der Rest der Hardware blieb gleich. Ab und an musste auch mal der Staub ausgeblasen werden.</p>
<p><img src="/images/nas_n40l.png" alt="N40L"></p>
<p>Ein <a href="https://cpu-benchmarks.com/cpu/amd-turion-ii-neo-n40l-dual-core/">AMD Turion II Neo (2 Core)</a> mit 1.5 GHz Takt und 2 GB RAM reichten aus, um ein minimales Linux zu betreiben. Die vier Platten im Software RAID 5 waren per NFS und SAMBA auf verschiedenene Clients eingebunden (Mac, Linux, Smart TV, Dokumentenscanner). Nach einiger Zeit wechselte ich auf <a href="https://www.truenas.com/">TrueNAS</a>, um eine hübsche GUI zur Administration zu haben und ohne viel Aufwand <a href="https://de.wikipedia.org/wiki/ZFS_(Dateisystem)">ZFS</a> zu nutzen. ZFS macht mit 2 GB RAM nicht besonders viel Spaß und so war die Hardware auch schnell an ihrem Maximum angekommen -- genügte aber dennoch einige Jahre für meine Zwecke.</p>
<p>Vor kurzem meldete eine Platte ungewöhnlich viele <a href="https://de.wikipedia.org/wiki/Self-Monitoring,_Analysis_and_Reporting_Technology">S.M.A.R.T.</a>-Fehler und ich musste sie aus dem RAID-Verbund lösen. Das macht natürlich immer etwas Bauchschmerzen, da nun die Last auf den verbleibenden Platten verteilt wird und bei einem weiteren Plattenausfall das RAID nicht mehr aufgebaut werden kann. Da die Platten (4x 2TB) generell ihre Lebenszeit überschritten hatten, beschloss ich, auch die Hardware komplett auszutauschen und ein neues NAS von Grund auf neu zu bauen.</p>
<h2 id="meine-anforderungen">Meine Anforderungen</h2>
<p>Das neue NAS sollte sich natürlich lohnen und wie meine geliebte N40L Box die nächsten 10+ Jahre unbemerkt seinen Dienst machen.</p>
<ul>
<li><strong>Mehr Power</strong>: Der Flaschenhals war oft die CPU, die TrueNAS-Oberfläche war teilweise sehr langsam. </li>
<li><strong>Mehr RAM</strong>: Damit ZFS wirklich sein Potential entfalten kann (deduplication, snapshots, ...), braucht es mehr als 2 GB RAM. Dem <a href="https://wiki.freebsd.org/ZFSTuningGuide">ZFS Tuning Guide</a> zufolge sollten es für mein Setup mindestens 8 GB sein.</li>
<li><strong>2x GBit LAN</strong>: Ein einzelnes Gerät sollte nicht die komplette Bandbreite beanspruchen. Neine Netzwerk-Hardware unterstützt <a href="https://de.wikipedia.org/wiki/Link_Aggregation">LACP</a>, so macht es Sinn, ein Mainboard mit zwei GBit LAN-Ports zu wählen.</li>
<li><strong>IPMI/iLO</strong>: Unzählige Male kniete ich im Keller auf dem Boden mit der Tastatur auf dem Schoß, nach dem ich umständlich einen Monitor angeschlossen hatte, nur um nachzusehen was beim Kernel-Update schief gelaufen ist oder warum das NAS immer noch nicht im Netzwerk auftaucht. Für die N40L gab es tatsächlich eine iLO-Karte, diese war aber unbezahlbar. <a href="https://de.wikipedia.org/wiki/Intelligent_Platform_Management_Interface">IPMI</a> habe ich auf der Arbeit lieben gelernt und diesen Luxus sollte das neue NAS mitbringen.</li>
<li><strong>HotSwap Bays</strong>: Das N40L hat zwar SATA-Platteneinschübe, doch sind diese nicht Hot-Plug-fähig. Warum, habe ich bis heute nicht verstanden (SATA ist <a href="https://superuser.com/questions/925847/demystifying-sata-hotplug">per Design</a> Hot-Plug-fähig, vermutlich eine schlecht konzipierte Steckverbindung), wollte es aber dennoch nie live testen. Das neue Gehäuse sollte dies auf jeden Fall ermöglichen, da ich bei einem Plattenausfall das Risiko nicht eingehen will, dass nach dem Reboot eine zweite Platte nicht mehr hochkommt (alles schon erlebt).</li>
<li><strong>Server-Komponenten</strong>: Ich möchte das NAS 24x7 laufen lassen, dazu soll die Hardware auch ausgelegt sein.</li>
<li><strong>TrueNAS</strong>: Als Betriebssystem/Distribution will ich weiterhin TrueNAS einsetzen, da ich damit nur gute Erfahrungen gemacht habe. Ich möchte mich zudem nicht selbst um die Aktualisierung der einzelnen Komponenten kümmern müssen -- was ich vermutlich aus Angst sowieso nie machen würde.</li>
<li><strong>Strombedarf</strong>: Bei all den Wünschen darf der Stromverbrauch der bisherigen Lösung (ca. 47 Watt) nicht steigen. Im Idealfall kann ich ihn durch entsprechende Wahl der Komponenten und etwas Software-Tuning noch etwas senken.</li>
<li><strong>Preis</strong>: Für eine Fertiglösung von Synology oder QNAP zahlt man ca. 500-600 EUR (ohne Platten). In einer ähnlichen Preisregion soll das selbst gebaute NAS auch liegen.</li>
</ul>
<h2 id="auswahl-der-komponenten">Auswahl der Komponenten</h2>
<p>Fertige NAS-Lösungen wie das <a href="https://www.synology.com/de-de/products/DS420+#specs">DS420+</a> von Synology oder das <a href="https://www.qnap.com/de-de/product/ts-451/specs/hardware">TS-451</a> von QNAP spielen preislich in dem Segment, in dem ich mich bewegen will. Allerdings sind beide Platzhirsche, rein auf die Hardware-Spezifikationen bezogen, sehr mager ausgestattet. Ein Intel Celeron mit 2.4 GHz und 1-4 GB RAM (je nach Modell) sind nicht gerade berauschend. Dies möchte ich wenn möglich überbieten.</p>
<h3 id="mainboard">Mainboard</h3>
<p>Das Herzstück legt den Rahmen für alle weiteren Komponenten fest. Deshalb ist es mir auch sehr schwer gefallen, hier einen geeigneten Kandidaten zu finden. Maßgebend sind Formfaktor, CPU-Sockel und die Ausstattung, die das Mainboard bereits ohne Zusatzkarten mitbringt. Der Formfaktor hängt vom Gehäuse ab, der CPU-Sockel von der verwendeten CPU. Also mussten diese im Vorfeld ausgewählt werden. Ich entschied mich für ein <a href="https://tweak.de/mainboard/mainboard-formfaktor-guide">microATX</a> Board mit <a href="https://en.wikipedia.org/wiki/LGA_1151">LGA 1151</a> Sockel (Details dazu direkt im Anschluss). Da ich bei meinem alten Arbeitgeber einige Erfahrungen mit Supermicro gesammelt habe und mich hier auch im Sortiment etwas auskenne, war die Wahl dann doch zügig auf das <a href="https://www.supermicro.com/en/products/motherboard/X11SSL-F">X11SSL-F</a> gefallen (nachdem der CPU Sockel feststand, dazu unten mehr).</p>
<p><img src="/images/mainboard.png" alt="Mainboard">
<em>Quelle: Supermicro X11SSL-F Motherboard Manual, Seite 10</em></p>
<p>Bei der Auswahl des Mainboards bin ich wie folgt vorgegangen: Auf der <a href="https://www.supermicro.com/en/products/motherboards/matrix">Motherboard Matrix</a> habe ich auf Generation <strong>X11</strong> und <strong>Single Sockel</strong> gefiltert. Über die Suchfunktion (<code>microatx lga 1151 dual lan</code>) habe ich die Ergebnisse auf microATX Formfaktor, LGA 1151 Sockel und zwei LAN Ports eingegrenzt. Zur Typenbezeichnung gibt es <a href="https://www.supermicro.com/products/Product_Naming_Convention/Naming_MBD_Intel_UP.cfm">hier</a> eine gute Erklärung. Es sollte also ein <code>X11SS*-F</code> sein (<code>X11</code> = Gen 11, <code>S</code> = Single CPU, <code>S</code> = Skylake CPU, <code>-F</code> = IPMI). Es kommen also das <code>X11SSL-F</code> (<code>L</code> = Kostenoptimiert) und das <code>X11SSM-F</code> (<code>M</code> = voll ausgestattet) in Frage, wobei jetzt nur noch die Frage im Raum steht, ob die L-Variante ausreicht oder ob es das voll ausgestattete Board sein soll. Ein Blick in <a href="https://www.supermicro.com/en/products/motherboard/X11SSL-F">beide</a> <a href="https://www.supermicro.com/en/products/motherboard/X11SSM-F">Datenblätter</a> verrät, dass dem X11SSL-F zwei SATA-Ports und ein PCI-E Steckplatz fehlen. Ein kurzer Preisvergleich macht die Entscheidung (bei ca. 100 EUR Unterschied) leicht.</p>
<p>Aber warum Supermicro? Es ist Server-Hardware, die für den Dauereinsatz konstruiert wurde (nicht wie ASUS, Gigabyte oder ASRock), die im Vergleich zu anderen Herstellern (HPE, DELL) günstig sind, eine große Auswahl haben, robust sind (ich habe hunderte Supermicro-Server administriert und nie war bei einem Ausfall das Mainboard das Problem) und es auf eBay einen regen Markt an gebrauchten Boards gibt. So habe ich für ca. 130 EUR (Neupreis ca. 300 EUR) ein <strong>X11SSL-F</strong> in top Zustand gekommen.</p>
<h3 id="cpu-k-hlung">CPU + Kühlung</h3>
<p>Doch nun zur Kernfrage: Warum unbedingt der <strong>LGA 1151</strong> Sockel? Meine Suche begann eigentlich mit der Auswahl einer leistungsfähigen aber dennoch sehr stromsparenden CPU. Hierzu sollten zuerst zwei Kennzahlen klar sein.</p>
<p>Was bedeutet <q>leistungsfähig</q>? Da CPUs in allen erdenklichen Formen existieren, kann man dies sehr schlecht an der Geschwindigkeit, Anzahl der Kerne oder Größe des Caches festmachen. Deshalb gib es Seiten wie <a href="https://www.cpubenchmark.net/">cpubenchmark.net</a>, welche eine definierte Test-Suite entwickelt haben. Diese wird von vielen freiwilligen auf deren Hardware ausgeführt und daraus ein Score (CPU Mark) errechnet -- welcher dann mit anderen CPUs verglichen werden kann. Mein Desktop-PC (AMD Ryzen 5 2400G) hat ein CPU Mark von 8730, die besten CPUs von AMD sind aktuell bei ca. 90.000; kosten aber auch ca. 5.000 EUR (nur die CPU!). Meine alte N40L hat ein CPU Mark von 589. Für das neue NAS wollte ich mich im Bereich von ungefähr 4000 bewegen.</p>
<p>Und was ist mit <q>stromsparend</q> gemeint? Im Idle-Mode verbraucht die CPU natürlich weniger als unter Volllast. Zudem haben die meisten CPUs verschiedene Stromsparoptionen wie Throttling, Power modes etc (C-States), welche sich im BIOS oder on-Demand verändern lassen. Auch hier ist es also schwer, den Begriff messbar zu machen. Eine Kenngröße ist der <strong>TDP</strong>-Wert (angegeben in Watt). Er beschreibt, wie viel Watt Abwärme von der CPU zu erwarten ist, wenn sie unter <q>echter Last</q> steht. Der Wert wird normalerweise verwendet, um das Kühlsystem der CPU richtig zu dimensionieren. Im Umkehrschluss heißt das aber auch, dass CPUs, die einen höheren TDP-Wert haben, auch mehr Strom verbrauchen. Das ist natürlich nur ein ungefährer Wert.</p>
<p>Nachdem ich die <a href="https://www.cpubenchmark.net/CPU_mega_page.html">CPU Mega Page</a> etwas durchgearbeitet hatte und mich etwas in diversen Foren eingelesen hatte, bin ich dann bei einer <a href="https://www.intel.com/content/www/us/en/products/sku/90728/intel-core-i36300t-processor-4m-cache-3-30-ghz/specifications.html">Intel i3-6300T</a> CPU hängen geblieben.</p>
<p><img src="/images/nas_cpu_cooling.png" alt="N40L"></p>
<p>Das ist eine <a href="https://ark.intel.com/content/www/us/en/ark/products/series/88394/6th-generation-intel-core-i3-processors.html">recht alte</a> CPU der 6ten Generation (siehe <a href="https://www.intel.com/content/www/us/en/processors/processor-numbers.html">hier</a> zum Verständnis der Typenbezeichnung), allerdings mit dem Zusatz <strong>T</strong>, was für eine Energieoptimierte Variante steht. Sie hat einen TDP von 35 Watt und unterstützt einige C-States, was zudem dafür spricht. Unterstützt ECC RAM und hat bei CPU Mark einen Wert von 4029. Also ein idealer Kompromiss von leistungsfähig und stromsparend. Da die CPU nicht mehr hergestellt wird, musste ich auch hier bei eBay zu einer gebrauchten Variante ausweichen (44 EUR).</p>
<p>Was die Kühlung angeht habe ich mich für den zum Mainboard passenden Kühlkörper entschieden (den SNK-P40046P von Supermicro). Dies ist nur der Kühlkörper ohne Lüfter. Da das Gehäuse (siehe unten) bereits gute Gehäuselüfter hat und ich nicht vor habe die CPU am Anschlag zu fahren wird das ausreichen. Bei der Wärmeleitpaste habe ich nicht gespart (MX-4). Kostenpunkt für Kühler und Paste: ca. 15 EUR.</p>
<h3 id="ram">RAM</h3>
<p>Beim RAM gab es nicht wirklich viel Spielraum. Hier gab das Mainboard/CPU schon den Typ an, war nur noch die Frage wie viel und ECC oder nicht. <a href="https://en.wikipedia.org/wiki/Error_correction_code">ECC</a> stellt sicher, dass einzelne Bit-Flips im RAM nicht mehr möglich sind. Macht also das System stabiler -- der sich im Preis bemerkbar macht.</p>
<p><img src="/images/nas_ram.png" alt="N40L"></p>
<p>Ich entschied mich für 2 neue 8GB DDR4 Module mit ECC von Samsung. Preislich machte das nur ca. 10 EUR zur nicht-ECC-Variante aus, also entschied ich mich dafür. Bezahlt habe ich für beide Module knapp 90 EUR.</p>
<h3 id="netzteil">Netzteil</h3>
<p>Da ich nicht vor habe, viel Strom zu verbrauchen, braucht es auch kein fettes Netzteil. Es sollte dennoch Kondensatoren eines Markenherstellers haben, also kein NoName-Produkt. Dies sind meist die ersten Komponenten, die kaputt gehen und dann zum Stromausfall führen. In Server-Hardware werden deshalb meist zwei redundante Netzteile verbaut, so dass der Defekt eines Netzteils nicht direkt zum Ausfall des Servers führt und im Live-Betrieb eines der beiden getauscht werden kann.</p>
<p>Sollte das NAS mal für ein paar Stunden wegen eines defekten Netzteils ausfallen, kann ich das verkraften. Ich entschied mich deshalb für ein günstiges <a href="https://web.archive.org/web/20200403113935/https://www.bequiet.com/en/powersupply/1284">350 Watt Netzteil von Be Quiet</a>.</p>
<h3 id="geh-use">Gehäuse</h3>
<p>Nun zu einem weiteren kontroversen Thema: Das Gehäuse. Hier gibt es unzählige Möglichkeiten. Selbstbau mit Gewindestangen und Alu-Profilen, ein altes Desktop-Gehäuse zweckentfremden, ein 19" Einschubgehäuse kaufen oder einfach alles in eine <a href="https://photo.mynethome.de/picture.php/P7270047/category/98-shackspace_stuttgart">Bratpfanne</a> legen.</p>
<p>Ein Selbstbau war mir zu aufwändig, ein Desktop-Gehäuse zu plump, eine Bratpfanne kann kein Hot-Swap (oder doch?). Ein 19" Gehäuse mit 4 Hot-Swap Bays wäre ideal gewesen, leider sind die allermeisten sehr tief und passen somit nicht in meinen Serverschrank im Keller. Deshalb schaute ich mich bei den Consumer NAS-Gehäusen um, von denen es mittlerweile eine Hand voll gibt.</p>
<p><img src="/images/nas_case2.png" alt="N40L"></p>
<p>SilverStone hat hier mit der <a href="https://www.silverstonetek.com/de/product/?page=1&kw=cs3">CS3XX-Serie</a> einige interessante Gehäuse im Sortiment. Besonders angetan hat es mir das <a href="https://www.silverstonetek.com/de/product/info/computer-chassis/CS351/">CS351</a>, für welches ich mich dann am Ende auch entschieden habe. Der Preis ist natürlich stattlich, doch habe ich vor das Gehäuse für die nächsten 10 Jahre zu nutzen.</p>
<h3 id="platten">Platten</h3>
<p>Hier habe ich ein günstiges Angebot abgewartet und 4x 4TB <a href="https://www.westerndigital.com/products/internal-drives/wd-red-plus-sata-3-5-hdd#WD40EFPX">WD Red Plus</a> 3.5 Zoll NAS Festplatten gekauft. Bezahlt habe ich für alle zusammen etwas mehr als 300 EUR. Die Platten will ich im RAID-Z2 betreiben, so können zwei Platten ausfallen, ohne dass sich das RAID in Luft auflöst.</p>
<p><img src="/images/nas_case.png" alt="N40L"></p>
<p>Dem Betriebssystem habe ich eine kleine, günstige 128GB SSD für ca. 15 EUR spendiert. Sollte diese kaputt gehen, ist auch dies kein Problem, da ich vor habe, eine fertige NAS-Distribution zu verwenden. Die Konfiguration kann ich einfach sichern und wieder herstellen.</p>
<p><img src="/images/nas_sata.png" alt="N40L"></p>
<h2 id="kosten">Kosten</h2>
<p>So, nun zur Übersicht der Materialkosten. Die einzelnen Komponenten (incl. Versandkosten) haben mich ca <strong>550 EUR</strong> gekostet. Nicht dabei die Festplatten. Einige Komponenten habe ich gebraucht (aber neuwertig) gekauft. Das Gehäuse mit fast 200 EUR fällt hier schon stark ins Gewicht. Wer also auf ein schickes Gehäuse verzichtet, bekommt das NAS für 350 EUR hin.</p>
<table><thead>
<tr>
<th style="text-align: left">Zustand</th>
<th style="text-align: left">Komponente</th>
<th style="text-align: right">Preis (EUR)</th>
</tr>
</thead><tbody>
<tr>
<td style="text-align: left">gebraucht</td>
<td style="text-align: left">SNK-P0046P CPU Heatsink</td>
<td style="text-align: right">9,99</td>
</tr>
<tr>
<td style="text-align: left">gebraucht</td>
<td style="text-align: left">Supermicro X11SSL-F Mainboard</td>
<td style="text-align: right">131,99</td>
</tr>
<tr>
<td style="text-align: left">gebraucht</td>
<td style="text-align: left">2x 8GB DDR4 ECC RAM</td>
<td style="text-align: right">89,90</td>
</tr>
<tr>
<td style="text-align: left">gebraucht</td>
<td style="text-align: left">Intel i3 6300T CPU</td>
<td style="text-align: right">44,00</td>
</tr>
<tr>
<td style="text-align: left">neu</td>
<td style="text-align: left">Silverstone SST-C351B Gehäuse</td>
<td style="text-align: right">192,89</td>
</tr>
<tr>
<td style="text-align: left">neu</td>
<td style="text-align: left">Be Quiet 350 Watt Netzteil</td>
<td style="text-align: right">34,84</td>
</tr>
<tr>
<td style="text-align: left">neu</td>
<td style="text-align: left">128 GB Patriot SSD</td>
<td style="text-align: right">14,28</td>
</tr>
<tr>
<td style="text-align: left">neu</td>
<td style="text-align: left">10x SATA 3 Kabel</td>
<td style="text-align: right">7,94</td>
</tr>
<tr>
<td style="text-align: left">neu</td>
<td style="text-align: left">MX-4 Wärmeleitpaste</td>
<td style="text-align: right">3,55</td>
</tr>
<tr>
<td style="text-align: left"></td>
<td style="text-align: left"><em>Versandkosten</em></td>
<td style="text-align: right"><em>21.98</em></td>
</tr>
<tr>
<td style="text-align: left"></td>
<td style="text-align: left"><strong>Summe</strong></td>
<td style="text-align: right"><strong>551,36</strong></td>
</tr>
</tbody></table>
<p><em>(Stand: 09/2022)</em></p>
<h2 id="aufbau">Aufbau</h2>
<p>Jeder der schon einmal einen PC selbst zusammengebaut hat, sollte hier keinerlei Probleme haben. Das meiste passt nur an einer Stelle in einer Richtung. Das Handbuch für das Mainboard hilft hier bei den Details. Auf eine Blende bei den hinteren Anschlüssen habe ich aus Kostengründen verzichtet.</p>
<p>Es lohnt sich, ein Firmware-Update für das BMC und das BIOS zu machen, sowie alles auf Werkseinstellungen zurückzusetzen. Das lässt sich bequem über das IPMI machen. Um alle Features nutzen zu können, benötigt man eine <a href="https://search.okoyono.de/search?q=+littlegoophy+supermicro+ipmi">super sichere Lizenz</a>. </p>
<h2 id="truenas">TrueNAS</h2>
<p>Über TrueNAS möchte ich hier nicht ausführlich schreiben. Es gibt einige NAS-Betriebssysteme, <a href="https://www.truenas.com/">TrueNAS</a> (ehemals FreeNAS) ist eine der bekannteren. <a href="https://www.truenas.com/download-truenas-core/">TrueNAS CORE</a> kann frei verwendet werden und ist Open Source. Bevor ich das NAS mit meinen Daten befüllt hatte, habe ich noch ein paar Experimente mit diversen Ausfall-Szenarios gemacht. Dafür mussten ein paar alte Platten herhalten. Konfiguriert in einem RAID-Z2 (weiter unten mehr dazu) ist das System extem ausfallsicher. Ich habe Platten im laufenden Betrieb gezogen, wieder eingesteckt, mehrere während eines großen Kopiervorgangs gezogen, an anderer Stelle wieder eingesteckt usw. Den RAID-Verbund habe ich nicht kaputt bekommen. Das gab mir noch einmal das (mir sehr wichtige) Vertrauen in ZFS und TrueNAS.</p>
<p>Zu zwei Aspekten möchte ich dennoch ein paar Details beschreiben. Die Art der Plattenkonfiguration und die Netzwerk-Anbindung.</p>
<h3 id="plattenkonfiguration">Plattenkonfiguration</h3>
<p>Da ich TrueNAS einsetze, ist <a href="https://de.wikipedia.org/wiki/ZFS_(Dateisystem)">ZFS</a> die Wahl des Dateisystems. Dies hat gegenüber <q>herkömmlichen</q> Dateisystemen ein paar Features, auf die ich nicht verzichten wollte. (Mehr dazu auf dem verlinkten Wikipedia-Artikel). Für mein Setup habe ich mich für ein RAID-Z2 entschieden, was einem RAID-6 entspricht. Hier können zwei Platten ausfallen, ohne dass es zum Datenverlust kommt. Sind alle Platten in einem großen Pool, lassen sich so beliebig viele Datasets anlegen/löschen. Wer hier einen besseren Einblick in ZFS haben möchte, kann sich die <a href="https://openzfs.github.io/openzfs-docs/Basic%20Concepts/index.html">OpenZFS Dokumentation</a> mal näher ansehen, es lohnt sich.</p>
<p>Für die Platten selbst bietet TrueNAS einige Energiesparoptionen an. Hier lassen sich Standby, Spindown, und andere Power Management Settings festlegen.</p>
<h3 id="netzwerk-konfiguration-lacp">Netzwerk-Konfiguration (LACP)</h3>
<p><img src="/images/nas_network.png" alt="N40L"></p>
<p>Wie weiter oben schon erwähnt, wollte ich auf jeden Fall eine gute Anbindung. An meinem Unifi Switch (USW-24-G1) lässt sich LACP einfach für bestimmte Ports konfigurieren. Wichtig dabei ist nur, dass die Ports fortlaufend sind. So habe ich bei mir Port 17 und 18 gewählt.</p>
<p><img src="/images/nas_lacp_unifi.png" alt="LACP unifi"></p>
<p>In TrueNAS lässt sich Link Aggregation unter <code>Network</code> -> <code>Interfaces</code> einstellen. Dies sieht bei mir wie folgt aus.</p>
<p><img src="/images/nas_lacp_truenas.png" alt="LACP truenas"></p>
<h2 id="fazit">Fazit</h2>
<p>Dies soll auf keinen Fall eine vollständige Nachbau-Anleitung für ein NAS darstellen. Ich habe hier nur meinen Prozess beschrieben, wie ich mir mein NAS zusammengestellt und aufgesetzt habe. Jeder hat andere Anforderungen und demnach auch andere Rahmenparameter. Deshalb habe ich die Auswahl der Hardware-Komponenten so ausführlich beschrieben. Ich hoffe, ich habe damit dem einen oder der anderen ein paar Tips für das eigene Setup gegeben.</p>
<p>Und habe ich meine Anforderungen an den Strombedarf erfüllt? Tatsächlich, ja. Das neue NAS misst bei normaler Nutzung ca. 35-45 Watt. Also sogar noch stromsparender als das alte, langsamere System.</p>
<p><em>Danke an <a href="https://social.okoyono.de/@mezzo">@mezzo</a> für die Verbesserungen und das Korrekturlesen.</em></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/284
2021-05-25T17:30:00+02:00
2021-05-25T17:30:00+02:00
Netzkultur: Permanent Netsplit
<p>Da geht es also dahin, das Freenode IRC Netzwerk.</p>
<p>Ich bin mit dem <a href="https://de.wikipedia.org/wiki/Undernet">Undernet</a> in die Welt des IRC eingetaucht. Anfangs noch mit einem <a href="https://botsin.space/@hackers_gifs/106244156225522737">recht peinlichem Nickname</a>, den ich dann nach ein paar Jahren immer mal wieder durch meinen Klarnamen ersetzt hatte, weil mir einfach keine passende Alternative einfiel.</p>
<p>Ich kämpfte gerade mit der Konfiguration von <a href="https://xfree86.org/">XFree86</a>. Nach vielen Stunden Fummelei und Gefluche suchte ich mir Hilfe im IRC. Und da passierte es wieder: Ich wurde mal wieder mit <a href="https://en.wikipedia.org/wiki/Aaron_Swartz">Aaron Swartz</a> verwechselt. Ich brauchte also einen Nick. Am besten sofort. Aus der Not heraus tippte ich schnell <code>/nick FU86</code> in die Eingabezeile, weil ich langsam die Faxen dicke hatte mit meinem XFree86-Setup. Die anderen in <code>#slackware</code> waren sehr geduldig und halfen mir, die grafische Oberfläche meiner Slackware-Installation endlich zum Laufen zu bringen.</p>
<p>Der Nick blieb, die anfänglich vulgäre Andeutung verblasste. Das war vor über zwei Jahrzehnten, noch vor der Dotcom-Blase und dem Web 2.0, als es noch weird und seltsam war, vor dem Computer abzuhängen und sich zum Geburtstag ein 56k-Modem zu wünschen. Als das Internet noch ein gut behüteter Raum von ein paar Nerds war.</p>
<p>Dann brach die Welt über das Internet herein, und es war für mich immer schwerer, meinen 4-Zeichen Nick zu registrieren (<a href="https://fu86.de/">fu86.de</a> ist aber immer noch meine :). Aus <code>fu86</code> wurde <code>fubar86</code>, <code>f0086</code>, <code>f00860</code>. Wo ich konnte, baute ich ein <q>fu</q> ein. Das iPad bekam den Namen <code>fuPad</code>, mein PC <code>deskFu</code> und der Hostname meines Notebooks ist immer noch <code>thinkFu</code>. Selbst das Autokennzeichen lässt eindeutig auf mich schließen.</p>
<p>Von den großen <q>sozialen</q> Plattformen wie Facebook, Twitter, Instagram habe ich mich abgemeldet. So bin ich heute meist schlicht unter <code>f</code>, dem Destillat meines ursprünglichen Nicks zu finden. Föderierte, dezentrale Systeme machen es möglich (dazu vielleicht in einem anderen Blogpost mehr). Mit IRC hat mein Nick nicht mehr viel gemeinsam, doch ist für mich einiges hängen geblieben, auch wenn äußerlich nur ein einziger Buchstaben daran erinnert.</p>
<p>In welchem IRC-Netzwerk man war, war nie wirklich von Bedeutung. Wichtig waren die User -- die Menschen an den Tastaturen. Ich schloss und pflegte Freundschaften, die bis heute bestehen, führte ernste Gespräche, alberte herum, chattete Nächte lang mit einer Person, die Jahre später meine Frau werden würde, half unzählichen Menschen bei Linux- und Programmier-Problemen, plante Projekte mit Freunden, schrieb Bots, konfigurierte <a href="http://psybnc.dk/www.psybnc.at/">Bouncer</a>, fand Hilfe und Trost. Im IRC fühlte ich mich Zuhause. Und ich konnte es überall hin mitnehmen, egal wo ich wohnte oder war.</p>
<p>Im Laufe der Jahre wurdes es ruhiger im IRC. Die meisten meiner Freunde und Weggefährten wechselten erst zu ICQ, dann kam Jabber, bis sie dann von WhatsApp, Telegram und den anderen großen Social Networks verschluckt wurden. Ich war natürlich als Early Adopter stets vorn mit dabei. Doch das Gefühl von Heimat verlor sich. Als würde die Stammkneipe nach Corona einfach nicht mehr existieren. Einige Jahre merkte ich das nicht einmal. Geblendet von schicken Apps die endlos nach unten scrollen, Sprachnachrichten aus der Hosentasche, <q>Eltern-WhatsApp-Gruppen</q>, personalisierte Timelines, Zusammenschnitte meiner besten Posts mit dem meisten Engagement, Herzchen, Favs, Likes, Daumen, Glocken und Swipes. Ich brauchte einige Jahre, um zu verstehen, dass es dabei hauptsächlich um die Monetarisierung meiner Aufmerksamkeit geht, nicht um die Kommunikation an sich.</p>
<p>Meinen Account bei Freenode habe ich <a href="https://social.okoyono.de/@f/106263526153892446">gelöscht</a>, <a href="https://arstechnica.com/gadgets/2021/05/freenode-irc-has-been-taken-over-by-the-crown-prince-of-korea/">die Umstände</a> machten die Entscheidung nicht schwer. Und offensichtlich ging es nicht nur mir so: Von allen Seiten bekam ich die Umbruchstimmung zu spüren. Es ist (wieder einmal) begeisternd zu sehen, wie aktiv und dynamsich ein paar Nerds sein können. Das mit Abstand größte IRC-Netz der Welt war innerhalb von 48 Stunden <a href="http://www.hinner.com/ircstat/Socip_F.html">wie leergefegt</a> und so gut wie alle Admins traten zurück. Die Freenode-Server gehen einer nach dem anderen vom Netz.</p>
<p><img src="/images/freenode.png" alt="Graph aktiver Nutzer und Channels auf Freenode"></p>
<p>Mein Zuhause ist schon lange nicht mehr das IRC. Ich bin ab und an kurz hier mal da, wenn ich ein Problem habe und kompetente Hilfe suche. Aber als Heimat bezeichne ich es nicht mehr. Mit <a href="https://matrix.org/">Matrix</a> habe ich für mich etwas gefunden, das dem IRC sehr nahe kommt. Und nicht nur das, IRC lässt sich sogar in Matrix integrieren -- wie so <a href="https://matrix.org/bridges/">vieles Andere auch</a>. So steht wieder der Mensch im Mittelpunkt, egal auf welchem IRC-Server oder Platform.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/283
2021-04-15T15:20:00+02:00
2021-04-15T15:20:00+02:00
Programme und OpenSource: Map Generator für "Der Kartograph"
<p>Wem der beigelegte Spielblock des Spiels Der Kartograph des Pegasus Spiele Verlags zu eintönig wird oder ihn schon leergespielt hat, hat mit dem <a href="https://kartograph.bitfummler.de/">Map-Generator</a> die Möglichkeit, neue Karten zu generieren. Die Standardwerte sind der Minivariante <q>Tabula Rasa</q> entnommen.</p>
<p><a href="https://pegasusshop.de/sortiment/spiele/kennerspiele/9791/der-kartograph-nominiert-kennerspiel-des-jahres-2020">Der Kartograph</a> ist die deutsche Variante des Spiels <a href="https://www.thunderworksgames.com/cartographers.html">Cartographers</a> von <a href="https://www.thunderworksgames.com/">Thunderworks Games</a>, herausgebracht von <a href="https://pegasus.de/">Pegasus Spiele</a>. Es wurde für das Kennerspiel des Jahres 2020 nominiert und macht sogar allein sehr viel Spaß.</p>
<p>Zusammen mit meiner Frau haben wir nun fast 50 Spielpartien hinter uns und die Map (der beigelegte Block) wird so langsam monoton. Und da die <a href="https://pegasusshop.de/Sortiment/Spiele/Kennerspiele/11523/Der-Kartograph-AEra-der-Helden">Erweiterungen</a> noch eine Weile auf sich warten lassen, sind wir zur <a href="https://pegasusshop.de/media/pdf/e4/06/e0/Kartograph_Minivariante_Tabula_Rasa.pdf">Minivariante <q>Tabula Rasa</q></a> gewechselt. Hierbei werden Gebirge, Ruinen und Ödnisfelder zufällig auf der Map verteilt.</p>
<p>Leider ist die Blanko-Map sehr ungeeignet um auf einem Laserdrucker ausgedruckt zu werden (alles sehr dunkel) und die Vorbereitung der Karte dauert fast 1/3 des ganzen Spiels. Aus diesen Gründen habe ich einen Map-Generator geschrieben, der das Erstellen von neuen Maps extrem vereinfacht und eine druckbare Version als SVG bereitstellt.</p>
<p><a href="https://kartograph.bitfummler.de/">kartograph.bitfummler.de</a></p>
<p>Der Generator erlaubt es, zufällige Karten nach dem <q>Tabula Rasa</q>-Regelwerk zu generieren. Dabei lassen sich die Parameter für die Anzahl von Ruinen, Berge und Ödland (fast) frei festlegen. Zudem lässt sich die Spielfeldgröße verändern, was das Spielerlebnis nochmal komplett verändert.</p>
<p><img src="/images/kartograph.png" alt="Generierte Kartograph Map mit veränderten Parametern"></p>
<p>Jede generierte Map ist über einen eindeutigen Seed <q>fixiert</q> (ähnlich wie bei Minecraft). So lassen sich die generierten Maps einfach weitergeben oder zu einem späteren Zeitpunkt noch einmal aufrufen und ausdrucken.</p>
<p>Der <a href="https://git.okoyono.de/f/kartograph-maps">Sourcecode</a> des Generators steht unter der GPLv3 und kann gerne für eigene Zwecke erweitert werden. Es sind zudem die Ausgabeformate SVG, ASCII und JSON möglich, um so eine Anbindung an andere Systeme zu realisieren oder einfach nur um die Map lokal auf der Console zu erstellen. Über Pull-Requests und Patches, aber auch besoners schöne Maps freue ich mich natürlich immer.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/281
2020-12-30T22:49:00+01:00
2020-12-30T22:49:00+01:00
Netzkultur: rC3 und økoyono
<p><img src="/images/rc3-map2.png" alt="map">
Das war sie also, die <a href="https://rc3.world/rc3/">rC3</a>. Wir als <a href="https://okoyono.de">økoyono</a> haben dieses jahr auch ein virtuelles Assembly beigesteuert, welches jeder mit einem Ticket für die rC3 besuchen konnte.</p>
<p>Wir wollten hier das Tileset komplett selbst erstellen. Als Inspiration für den Style bedienten wir uns bei <a href="https://100r.co/">100 Rabbits</a>, den <a href="https://kodanshacomics.com/series/blame/">BLAME Comics</a> und der <a href="https://duckduckgo.com/?q=zelda+gameboy+dungeon&t=ffab&atb=v208-1&iar=images&iax=images&ia=images">Dungeons in Zelda auf dem GameBoy</a>.</p>
<p><img src="/images/rc3-map3.png" alt="map"></p>
<p>Da wir bis kurz vor Schluss nicht wussten, was in der 2D-Welt möglich sein wird, haben wir großzügig geplant.</p>
<p><img src="/images/rc3-map.png" alt="map"></p>
<p>Mit <a href="https://www.mapeditor.org/">Tiled</a>, <a href="https://www.gimp.org/">Gimp</a> und <a href="https://krita.org/en/">Krita</a> machten wir uns ans Werk und bauten Raum für Raum. Dabei erweiterten wir das Tileset kontinuierlich. Allerdings noch ohne Inhalt. Hier fehlte Schlicht noch die Info in der <a href="https://howto.rc3.world/maps.html">Anleitung</a>.</p>
<p><img src="/images/rc3-world4.png" alt="map"></p>
<p>Als dann die Welt an Tag 2 einigermaßen stabil lief, konnten wir sie mit etwas Leben füllen. Wir verlinkten unsere aktuellen Projekte, ein paar befreundete Assmeblies/Hackerspaces, ein rudimentäres <a href="https://pad.okoyono.de/p/rc3">Gästebuch</a> und zwei Badges. Die Badges wurden auch gut gefunden (30 und 44 mal), obwohl einer davon gut versteckt war!</p>
<p>Wir hätten gerne noch mehr gemacht, allerdings fehlte es dann etwas an der Zeit. Sollte das Konzept ein weiteres Mal zum Einsatz kommen, haben wir schon einige Pläne für einen Ausbau.</p>
<p>Ich hab es leider nicht geschafft, all zu viele Vorträge anzusehen, aber glücklicherweise gibt es auf <a href="https://media.ccc.de/c/rc3?sort=view_count">media.ccc.de</a> (bald) fast alles zum nachsehen.</p>
<p>Meine persönlichen Highlights/Watchlist:</p>
<ul>
<li><a href="https://media.ccc.de/v/rc3-11496-1up_analoge_rebellion_2_0">1UP -- Analoge Rebellion 2.0</a></li>
<li><a href="https://media.ccc.de/v/rc3-729538-die_geschichte_der_corona_warn_app">Die Geschichte der Corona-Warn-App</a></li>
<li><a href="https://media.ccc.de/v/rc3-3-access_to_tools_in_graphic_design#t=1328">Access to Tools (in Graphic design)</a></li>
<li><a href="https://media.ccc.de/v/rc3-11400-building_blocks_of_decentralization">Building Blocks of Decentralization</a></li>
<li><a href="https://media.ccc.de/v/rc3-857362-die_rosarote_brille_des_fediverse">Die rosarote Brille des Fediverse</a></li>
<li><a href="https://media.ccc.de/v/rc3-11367-inside_xhamster">Inside xHamster - Undercover im Löschteam von Deutschlands meistbesuchter Pornoseite</a></li>
<li><a href="https://media.ccc.de/v/rc3-771575-rueckblick_auf_die_deutsche_ratspraesidentschaft">Rückblick auf die deutsche Ratspräsidentschaft</a></li>
<li><a href="https://media.ccc.de/v/rc3-594508-linux_remixen_ich_back_mir_eine_linux_distro">Linux remixen</a></li>
</ul>
<p>Auch wenn vieles etwas holprig war, für den ersten (wirklich großen) kontaktlosen Kongress war das eine beeindruckende Leistung (wer wissen will was im Hintergrund ablief, sollte sich das <a href="https://media.ccc.de/v/rc3-11585-infrastructure_review">Infrastruktur Review</a> ansehen). Es gibt Verbesserungspotential, aber die Messlatte liegt schon mal ganz weit oben! Danke an alle, die das möglich gemacht haben.</p>
<p><strong>Update 2021-01-04</strong>: Ein paar Mitschnitte sind verschwunden, diese habe ich auch hier entfernt.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/280
2020-11-14T21:50:00+01:00
2020-11-14T21:50:00+01:00
Programme und OpenSource: fuNote
<p>fuNote ist ein Experiment, wie weit ich mit ANSI C99, dem <a href="https://de.wikipedia.org/wiki/Portable_Operating_System_Interface">POSIX</a> Interface und den Linux Manpages komme. Inspiriert durch <a href="https://viewsourcecode.org/snaptoken/kilo/index.html">kilo</a> entstand ein einfacher und sehr kompakter Text-Editor mit Syntax Highlighting für C.</p>
<p>Der minimalistische Editor ist ein (fast) POSIX-Kompatibler Terminal-Editor, welcher in allen bekannten (und nicht so bekannten) VT100-Kompatiblen Terminal-Emulatoren funktioniert. Es wurden keinerlei Abhängigkeiten oder Libs wie ncurses verwendet.</p>
<p><img src="/images/funote.png" alt="fuNote mit einem C Sourcecode"></p>
<h2 id="features">Features</h2>
<ul>
<li>Laden/Speichern/Erstellen von Textdateien (ASCII)</li>
<li>Bekannte Tastaturkürzel</li>
<li>Suche</li>
<li>Syntax-Highlighting für C</li>
<li>Superklein (23kb Binary)</li>
<li>Keinerlei Abhängigkeiten</li>
</ul>
<h2 id="download">Download</h2>
<p>Wer selbst mit dem Sourcecode spielen möchte oder sich ein Ticket aus dem Issue-Tracker vornehmen möchte, ist herzlich dazu eingeladen. Der <a href="https://git.okoyono.de/f/fuNote">Code</a> steht under der MIT Lizenz.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/277
2019-09-10T20:00:00+02:00
2019-09-10T20:00:00+02:00
Mobile Apps: Toolheim
<p>You love to play <a href="https://en.wikipedia.org/wiki/Mordheim">Mordheim</a>, the skirmish variant of the Warhammer tabletop game from Games Workshop. But managing the roster sheets is tedious, especially when you play in a group larger than two. The <a href="https://play.google.com/store/apps/details?id=net.aaronfischer.toolheim">Toolheim</a> app for Android is your companion tool to manage warband rosters within a team and track progress -- nerd style!</p>
<h2 id="overview">Overview</h2>
<p>The traditional way of creating a warband roster (your warband configuration) consists of filling out the <a href="http://broheim.net/downloads/resources/Mordheim%20Roster%20v3.pdf">warband roster sheet PDF</a> by hand or with a PDF editing tool. After each battle, you have to update your roster sheet, which essentially means to fill out the complete form once again from scratch. Most likely you want to share each state of your warband PDF with your friends, so they can see, verify and compare it. After some battles, this process gets tedious and kills most of the fun.</p>
<p><img src="/images/mordheim.png" alt="warband"></p>
<p>Toolheim try to fix this with a little bit of technology. Warband roster sheets will be described in a structured way, so other tools can read, manipulate and work with it. The Toolheim app can read this files from a GitHub repository and display it. This sounds way too abstract, so lets just dive into the warband roster files.</p>
<h2 id="warband-file-syntax">Warband File Syntax</h2>
<p>Here you see a simplified version of my Dwarf Rangers warband:</p>
<pre><code class="language-yaml">warband: The Revolting Dwarfs (Dwarf Rangers)
active: true
campaign: 11
objective: The Lure of Fortune
alignment: Lawful/Neutral
achievments: |
Land Train
Silk Trader
gc: 78
shards: 2
equipment: Lucky Charm
# Max 12 units
# Racial maximums (Dwarfs):
# M3, WS7, BS6, S4, T5, W3, I5, A4, Ld10
heros:
- hero: Ragnar (Runesmith) [32XP]
stats: M3, WS5, BS4, S3, T4, W1, I3, A1, Ld10, Sv-
skilllists: Combat, Shooting, Academic, Strength, Special
weapons: Axe, Axe
armour: Helmet, Light Armour, Lucky Charm
rules: Leader, Blackblood, Expert Axeman, Tactician
warbandaddition: 5
# Skill wishlist:
# * Two-Weapon Master (ignore -2 to hit for offhand)
# * Money Maker (D6+2 GC after battle)
- hero: Floki (Apprentice Runesmith) [8XP]
skilllists: Combat, Academic, Strength, Special
stats: M3, WS3, BS2, S3, T4, W1, I2, A1, Ld9, Sv-
weapons: Axe, Dagger
armour: Helmet, Heavy Armour, Lucky Charm
rules: Extra Set Of Hands, Skilled Driver, Haggle
- hero: Bjørn (Troll Slayer) [15XP]
skilllists: Combat, Academic, Strength, Special
stats: M3, WS5, BS3, S3, T4, W1, I2, A1, Ld10, Sv-
weapons: Dwarf Axe, Dagger
armour: Lucky Charm
rules: Deathwish, Fearsome
henchmen:
- group: Beard Mob (4 Beardling) [13XP]
stats: M3, WS4, BS3, S3, T4, W1, I2, A2, Ld8, Sv-
weapons: Axe, Dagger
armour: Helmet
notes: |
Inscribe rune before battle
Blackblood (Ragnar) = Anyone in base contact got S3 hit if wounded
</code></pre>
<p>As you might already notice, the warband definition file is valid <a href="https://yaml.org/">YAML</a> syntax with some constraints. To read a warband file, you just need a YAML parser and some regular expressions. There are some fields which need to be in a specific format. Here are all specialties:</p>
<ul>
<li>warband: <code><name of the warband> (<race>)</code> <em>Will define the name of the warband (you can choose whatever you want) and the chosen race (see the rule book)</em>.</li>
<li>equipment/skilllists/armour/weapons/rules/injuries: <code><name>, <another name>, ...</code> <em>These are all lists of names, separated with a comma. The name itself can be anything, but it makes sense to use the identifiers from the rulebook to be consistent</em></li>
<li>hero: <code><name of the hero> (<type>) [<experience>XP]</code> <em>A hero consists of a name (choose an epic one!), a type (see what your race has to offer) and the experience gained on the battleground.</em></li>
<li>group: <code><name of the henchmengroup> (<number> <type>) [<experience>XP]</code> <em>Similar to a hero, a henchmen group consists of a name, a type and the gained experience. The number specifies the unit count of the group.</em></li>
<li>stats: <code>M<movement>, WS<weapon skill>, BS<ballistic skill>, S<strength>, T<toughtness>, W<wounds>, I<initiative>, A<attacks>, Ld<leadership>, Sv<save></code> <em>The core of the list are the stats for heros, hired swords and henchmen groups. The stats define the characteristics of the unit.</em></li>
</ul>
<p>In addition to that, there are some flags you can set for heros and henchmen groups, which are:</p>
<ul>
<li>warbandaddition: <code><number of GC which the unit adds to the rating></code> <em>Some units increase the warband rating mor than usual. Big or very expensive units for example. The be flexible with that, you can use this to adjust the warband rating.</em></li>
<li>hiredsword: <code><true, if the unit is a hired sword></code> <em>Hired Swords do count a little differently, you need to pay upkeep and so on. If you have hired swords in you warband, flag it appropriately.</em></li>
<li>slowwitted: <code><true, if the unit is slowwitted></code> <em>Some units are a little dumb (trolls, beasthounds, ...), which take twice as much experience to advance.</em></li>
</ul>
<p>The rest of the keys are self explanatory. Missing keys will be filled out with default values.</p>
<h2 id="github">GitHub</h2>
<p>So, what is the whole deal about the warband roster files? Why not just fill out the PDF and save each revision? The warband roster files (and text files in general) have the big advantage of versioning, comparing and sharing -- for free! Text files can be managed in a git repository, which will track the changes and make it easy to see the changes. For that, Toolheim uses GitHub to store the warband files. Here is <a href="https://github.com/Labernator/Mordheim/commit/d59a0b6d1f2cb3f5a30903578e9d03803aa8cca7">an example of a post game sequence</a>. The changes to the warband are shown easily.</p>
<h2 id="the-toolheim-app">The Toolheim App</h2>
<p>And here comes the Toolheim app into play. The app searches a GitHub repository for suitable warband files (<code>.warband.yml</code> extension), parse it's content and display it. That's basically it. There are some nice features and a stable sync mechanism, so you see which warbands have changes since you last watched it. The app will give you an overview of your own warband and the warbands of your playmates.</p>
<p>To search for warbands on a GitHub repo, the folder structure need to be in a specific way. Take the <a href="https://github.com/f0086/toolheim-example">example repository</a> as a quick reference. The warband files need to be in the root folder or a sub directory (which can be specified in the app). Each directory name represents a player, so name it accordingly. In this directories there can be one or more <code>.mordheim.yml</code> files. Use the <code>active: true</code> flag to enable/disable warbands. Specify the GitHub repository, the optional sub directory and click the <q>Start search</q> button to crawl the repo for warbands.</p>
<p><img src="/images/toolheim1.png" alt="toolheim app"></p>
<p>When finished, you can see all found warbands in the drawer, ordered by CP. If you hit the sync button (the left button on the top right), all found warbands will be checked against new commits. If there are changes, the warband name will be shown in bold.</p>
<p><img src="/images/toolheim2.png" alt="toolheim app"></p>
<p>The warband name can be clicked to see it's units and their stats. Heros are green, henchmen groups are orange and hired swords are black. Red colored stats stood out and purple stats are exceptional. This makes it easy to quickly see the important parts of the unit.</p>
<p>The calculated rating and rout limit will be shown at the top. If you scroll to the bottom, there are some less important information like the objective or stored equipment.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/269
2019-03-26T20:55:00+01:00
2019-03-26T20:55:00+01:00
Das 18650 Experiment: Teil 4/4: Off-Grid mit 18650 Zellen
<p>Mit einem Akkupack selbst lässt sich nun so einiges anstellen. Allerdings gibt es noch zwei Dinge zu bedenken: Zum einen, wie man den Akku aufladen möchte und zum anderen was man damit betreiben will. Als Stromquelle will ich eine Solarzelle verwenden. Der Akku selbst soll dann am Ende eine Lichterkette (12V) im Freien betreiben. Um all das zu erreichen, benötigen wir etwas zusätzliche Elektronik. Darum geht es im letzten Artikel der 18650-Reihe.</p>
<p>Um den Akkupack von der Sonne aufzuladen und dann eine konstante Spannung abzugeben, werden die folgenden Komponenten benötigt:</p>
<p><img src="/images/18650-4-1.png" alt="schemaaufbau"></p>
<h2 id="solarstrom">Solarstrom</h2>
<p>Solarzelle ist nicht gleich Solarzelle. Man unterscheidet grundlegend zwischen Monokristallin und Polykristallin. Zudem macht es in den wenigsten Fällen Sinn, eine Zelle einzeln zu kaufen. Meistens wird ein ganzes Panel angeboten, in dem mehrere Zellen in Reihe bzw. parallel verbaut sind. Es kommt also auf die Typ und die Anordnung der Zellen an.</p>
<p>Monokristallin-Panels (meist schwarz) haben im Vergleich zu Polykristallin-Panels (meist blau) einen höheren Wirkungsgrad (14-20% zu 12-16%), sind aber in der Herstellung teurer. Allerdings <q>altern</q> Polykristallin-Panels nicht so schnell, da sie nicht so heiß werden. Monokristallin-Panels hingegen erzeugen auch bei schlechten Lichtverhältnissen noch Strom und sind deshalb bei schlechtem Wetter effektiver.</p>
<p>Es ist also eine Frage des Anwendungszwecks, für was man sich entscheidet. Hat man viel Fläche und plant die Solarzelle für die nächsten Jahrzehnte einzusetzen, ist man wohl mit Polykristallin-Panels besser bedient. Will man mehr Leistung auf kleinerem Raum und zahlt gerne einen kleinen Aufpreis dafür, sind Monokristallin-Panels die bessere Wahl. Ich habe mich bei meinem Projekt für ein Monokristallin-Panel entschieden.</p>
<p>Dann ist die Anzahl und die Anordnung der einzelnen Zellen entscheidend. Sie bestimmen, wie viel Spannung und wie viel Watt in Spitze erreicht werden können. Im normalen Betrieb sind die beiden Kennzahlen natürlich die meiste Zeit niedriger. Doch wie viel niedriger? Laut dem deutschen Wetterdienst gibt es im Schnitt <strong>1640 Stunden Sonne im Jahr</strong>. Eine Beispielrechnung für ein 10 Watt Solarpanel mit 12V:</p>
<pre><code>1640 Stunden * (10 Watt / 12 Volt) = 1366 Ah im Jahr (~3700mAh am Tag)
</code></pre>
<p>Im Idealfall können wir also ca. 3700mAh am Tag aus dem Akku entnehmen, ohne dass wir Gefahr laufen ihn leer zu machen. Im Umkehrschluss dauert dann eine komplette Ladung eines 40Ah Akkupacks ca. 11 Tage. In der Praxis scheint natürlich nicht jeden Tag die Sonne und im Winter gibt es weniger Tageslicht als im Sommer. Der Wert dient eher als Richtwert für die Dimensionierung des Solarpanels.</p>
<h2 id="den-akku-laden">Den Akku laden</h2>
<p>Haben wir die Stromquelle (Solarpanel), müssen wir zunächst dafür sorgen, dass wir eine konstante Spannung haben, mit der wir den Akku laden können. Dies können wir mit einem Boost-Converter erreichen. Besser geht es allerdings mit einer MPPT-Schaltung. Der <a href="https://de.wikipedia.org/wiki/Maximum_Power_Point_Tracking">Wikipedia-Artikel</a> dazu ist so gut, dass ich mir hier die Erklärung spare.</p>
<p>Natürlich gibt es hierfür schon <a href="https://www.ebay.de/sch/i.html?_from=R40&_trksid=m570.l1313&_nkw=12V+MPPT+Solar+Panel+Controller+3Series+Lithium+18650+Battery+Charging+Module+mj&_sacat=0">fertige Schaltungen</a>, die man zwischen Solarpanel und Akkupack anschließt. Beim Kauf sollte man allerdings darauf achten, dass die Schaltung zum Solarpanel (Ladestrom, Spannung) und zum Akkupack (Typ, Spannung) passt.</p>
<p><img src="/images/18650-4-2.png" alt="anschluss MPPT"></p>
<p>Die Ladeplatine sorgt dafür, dass der Akku zur richtigen Zeit mit dem richtigen Strom/Spannung geladen wird. Die von mir verwendete Platine hat sogar zwei Status-LEDs, die den aktuellen Zustand anzeigen.</p>
<h2 id="konstante-stromquelle">Konstante Stromquelle</h2>
<p>Ist der Akku geladen, können wir damit etwas betreiben. In den meisten Fällen benötigt man allerdings eine konstante Spannung, egal wie voll/leer der Akku ist. Hierfür kann man einen Buck/Boost-Konverter (auch <a href="https://de.wikipedia.org/wiki/Abw%C3%A4rtswandler">Auf-/Abwärtswandler</a> genannt) verwenden. Dieser regelt die Spannung auf einen definierten Wert. </p>
<p><img src="/images/18650-4-3.png" alt="anschluss buck boost converter"></p>
<p>Angeschlossen wird er zwischen Akkupack und Verbraucher. Über ein Potentiometer (meist ein kleiner blauer Klotz mit einer Schraube) lässt sich dann die gewünschte Spannung einstellen. Bei mir genau 12V.</p>
<p>Wichtig dabei ist allerdings, dass der Wandler nur dann am Akkupack hängt, wenn er auch benötigt wird. Da er selbst im Leerlauf Strom verbraucht, kann er den Akkupack auch ohne einen angeschlossenen Verbraucher leer machen. Deshalb lohnt es sich, einen Schalter einzubauen -- speziell dann, wenn nur ein recht kleines Solarpanel verwendet wird.</p>
<h2 id="mechanischer-schutz">Mechanischer Schutz</h2>
<p>Jetzt stellt sich nur noch eine Frage: Wohin mit all dem? Da der Akkupack trotz Vorkehrungsmaßnahmen wie Sicherungsdraht, BMS und MPPT-Schaltung weiterhin eine Brandgefahr darstellt, sollte man sich zwei mal überlegen, wo man ihn unterbringt. Auf jeden Fall sollte man den Akku und die Elektronik vor mechanischen Einflüssen sowie vor Wasser/Feuchtigkeit schützen. Eine Holzkiste ist hier ungeeignet. Idealerweise eine Metallbox, die sich Wasserdicht verschließen lässt. Im Elektrobereich gibt es hier einiges zur Auswahl: Schwer entflammbare Plastikboxen oder gleich eine Aluminium-Box, welche sich Wasserdicht verschließen lässt. Suchbegriffe für eBay wären <q>projektbox</q> oder <q>elektronik gehäuse</q>.</p>
<h2 id="fazit">Fazit</h2>
<p>Wir haben Zellen <a href="/erd">aus alten Akkus geschält</a>, <a href="/hmx">aufwändig recycled</a> und daraus einen <a href="/nde">Akkupack gelötet</a>. Mit dem Akkupack können wir nun alles Mögliche betreiben. Hat sich der Aufwand gelohnt? Ich meine ja. Auch wenn der Recycling-Prozess sehr zeitaufwändig ist, kann man so nicht nur viel Geld sparen, sondern auch der Umwelt etwas gutes tun. Bei der Herstellung von Akkus werden viele seltene Rohstoffe benötigt, die dann beim Recycling aufwändig wieder <a href="https://de.wikipedia.org/wiki/Batterierecycling">zurückgewonnen werden</a> müssen. Einem Akku also ein zweites Leben schenken, macht also nicht nur finanziell Sinn.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/259
2019-02-22T22:30:00+01:00
2019-02-22T22:30:00+01:00
Im Detail erklärt: Clojure Template Engines
<p>Für Webanwendungen gibt es in jeder Sprache zwei bis drei große Frameworks, die man verwenden sollte. Sie ersparen Zeit, vermeiden viele Anfängerfehler und stellen die Basisfunktionalität bereit (meist ein MVC Pattern). Gerade bei PHP (Symfony, Laravel, CodeIgniter), Python (Django, Pyramid, Flask) und Ruby (Rails, Sinatra, Hanami) sind sie sehr beliebt. In Clojure-Land funktioniert die Welt anders: Hier ist man selbst verantwortlich dafür, sich seine Komponenten nach eigenem Geschmack zusammenzusuchen. Die Basis ist <a href="https://github.com/ring-clojure/ring">Ring</a>, ein Abstraktionslayer für HTTP (ähnlich wie Rack für Ruby). Es gibt ein paar Meta-Frameworks wie Luminus, Pedestal oder Macchiato, welche im Kern allerdings auch nur ein Zusammenschluss von Bibliotheken mit einer Beschreibung sind. Es lohnt sich also, sich mit Ring und den zur Verfügung stehenden Bibliotheken zu beschäftigen. Da das Rendern von HTML einer der Hauptaufgaben einer Webanwendung ist, mache ich hier einen Rundumschlag der bekannteren Template Engines für Clojure.</p>
<h2 id="selmer">Selmer</h2>
<p>Wer bereits mit Symfony gearbeitet hat, ist zwangsläufig mit <a href="https://twig.symfony.com/">Twig</a> in Berührung gekommen. <a href="https://github.com/yogthos/Selmer">Selmer</a> ist stark an die Syntax von Twig angelehnt und stellt eine klassische Template Engine bereit. Eine harte Trennung zwischen dem Template (gespickt mit Selmer-Syntax) und dem Controller/Model. Selmer nimmt eine HTML-Datei und ein Set von Variablen entgegen und gibt das gerenderte HTML aus. Ein simples Beispiel:</p>
<pre><code>(selmer.parser/render "Hallo {{ name }}" {:name "Aaron"})
</code></pre>
<p>Diese Trennung von Anwendungslogik und Template hat eine lange Geschichte. Konzeptionell ist der Gedanke beim MVC Pattern (Model View Controller), dem üblichen Spathetti-Code entgegenzuwirken, den man in alten Perl- und PHP-Anwendungen findet. MVC macht den Code übersichtlicher und modularer. Zudem war wohl der Wunschgedanke, dass Datenbankspezialisten das Model bereitstellen, Designer die View und der Programmierer dann die Anwendungslogik des Controllers. Mit diesem Gedanken sind auch die meisten Template Engines entstanden; Sie wurden absichtlich so simpel wie möglich gehalten, damit auch Leute ohne Programmierkenntnisse Templates erstellen können (und dass man nicht auf die Idee kommt, Anwendungslogik in die View zu stopfen). Aus dieser Wunschvorstellung ist aber leider nie etwas geworden. In den allermeisten Fällen erstellt eine Person sowohl die Anwendungslogik als auch das Template (zumindest in meinen bisherigen Jobs der letzten 15 Jahre).</p>
<p>Und hier liegt dann schon die erste Schwachstelle von Selmer (und allen anderen traditionellen Template Engines): Die Trennung von Controller und View erzwingt es, dass ein Übergang von Anwendungs-State stattfinden muss. Der Controller übergibt ein Set von (vorbereiteten) Variablen an die View. Die Template Engine nimmt die Variablen entgegen und interpretiert das Template zusammen mit den gegebenen Variablen. Heraus kommt das fertige HTML. Die Template Engine selbst hat keinen State, nur die Variablen, die übergeben werden. Beim Erstellen des Templates ist man also auf die Template Engine und deren Syntax beschränkt.</p>
<p>Das macht sich bei Selmer besonders schnell bemerkbar. Selbst simple Sachen wie eine Schleife die n mal durchläuft, ist nicht möglich. Auch der Zugriff auf den Anwendungs-State ist nur durch viel Umstand (<a href="https://github.com/yogthos/Selmer/blob/master/src/selmer/parser.clj#L79">add-tag!</a> und <a href="https://github.com/yogthos/Selmer/blob/master/src/selmer/filters.clj#L544">add-filter!</a>) machbar. Prinzipiell gibt es eine Hand voll Kontrollstrukturen wie <code>for</code> oder <code>if</code> und die Möglichkeit Variablen auszugeben. Mit <a href="https://github.com/yogthos/Selmer/blob/master/src/selmer/filters.clj#L97">Filter</a> lässt sich die Ausgabe dann mit einer Art Pipeline verändern.</p>
<pre><code>Hallo {{ name|lower|replace:o:0 }}!
</code></pre>
<h2 id="enlive">Enlive</h2>
<p>Der Umstand mit der Template-Sprache hat Enlive ganz geschickt umgangen, in dem das Template nur aus reinem HTML besteht. Es gibt keine Platzhalter und auch keine Schleifen oder Fallunterscheidungen. Der Trick dabei ist, das HTML-Template mit Hilfe von CSS-Artigen Selektoren zu selektieren und anschließend mit Clojure zu bearbeiten und zu ersetzen. Das hört sich im ersten Moment eklig an, ist aber ziemlich genial. Ein Beispiel:</p>
<pre><code>(require '[net.cgrand.enlive-html :as html])
(html/deftemplate projects-template "templates/index.html"
[name]
[:head :title] (html/content (str "Hello " name)))
(apply str (projects-template "Aaron"))
</code></pre>
<p>Mit dem <code>html/deftemplate</code> Makro wird ein neues Template angelegt -- genauer gesagt eine Funktion. Der erste Parameter ist das HTML-Template, der zweite dient als Übergabeparameter und alle folgenden definieren die Selektoren und deren Aktionen. <code>html/content</code> ist hier ein Transformator, der den Inhalt des selektierten DOM-Node ersetzt. Die daraus resultierende Funktion kann überall aufgerufen werden. Die Rückgabe ist das fertig gerenderte HTML in Form einer Sequenz aus Strings (deshalb das <code>apply</code>).</p>
<p>Die View und der Controller sind hier wie bei den gewöhnlichen Template Engines getrennt. Besser noch, das Template enthält gar keinen Code mehr. Die Mächtigkeit wird an einem größeren Beispiel (aus <a href="https://git.okoyono.de/mezzomix/buch_des_monats/src/branch/master/src/buchdesmonats/core.clj">einem Projekt</a> das mittlerweile fast 5 Jahre besteht) deutlicher:</p>
<pre><code><!DOCTYPE html>
<html>
<head>
<title>okoyono.de -- Buch des Monats</title>
</head>
<body>
<div id="covers">
<div class="cover-item">
<a href="#">
<img src="#" alt="LovelyBooks cover" title="Book title">
</a>
</div>
</body>
</html>
</code></pre>
<p>Hier der Controller:</p>
<pre><code>(html/defsnippet cover-item-model "buchdesmonats/layout.html"
[:div#covers :> :div]
[link title]
[:a] (html/set-attr :href link)
[:img] (html/set-attr :src (url->file link "book-covers") :title title))
(html/deftemplate index-template "buchdesmonats/layout.html"
[cover-urls]
[:#covers] (html/content
(map #(cover-item-model % "zu Lovely Books")
cover-urls)))
(apply str (index-template list-of-urls))
</code></pre>
<p>Das HTML-Template besteht aus einem Div <code>covers</code>, welches wiederum ein beispielhaftes <code>cover-item</code> enthält. Mit <code>html/defsnippet</code> bauen wir uns eine Funktion, die den beispielhaften Eintrag eines Buch-Covers repräsentiert. Die beiden Parameter <code>link</code> und <code>title</code> werden dann im Template mit <code>html/set-attr</code> in den Link und das Bild eingesetzt. In anderen Template Engines wird dieser Mechanismus gern als <q>Partial</q> bezeichnet, in Enlive wird daraus eine ganz gewöhnliche Funktion mit Parametern. Diese Funktion wird dann gleich in <code>index-template</code> verwendet.</p>
<p>Wie man sieht, existiert gar keine Template-Sprache mehr. Sie wurde von Clojure abgelöst. Somit steht dem Programmierer die volle Power der Programmiersprache zur Verfügung und ist nicht auf ein limitiertes Set von Sprachelementen aus einer Template-Sprache wie Selmer oder Twig angewiesen.</p>
<p>Enlive ist im Grunde ein HTML-Parser und -Generator mit ein paar Hilfsfunktionen. Den Parser-Teil kann man allerdings nicht nur für die eigenen Templates einsetzen, sondern für jede Art von Webseite. So lässt sich ganz einfach ein Web Scraper bauen. Hier ein kleines Beispiel aus demselben Projekt von oben:</p>
<pre><code>(-> (java.net.URL. lovelybooks-url)
html/html-resource
(html/select [:img.ResponsiveImage.BookCover])
first
(get-in [:attrs :srcset])
(str/split #" "))
</code></pre>
<p>Das sieht nach nicht viel aus, macht aber eine ganze Menge. An <code>lovelybooks-url</code> ist ein String mit einer URL gebunden, der mit <code>html/html-resource</code> zu einer Quelle für Enlive umgewandelt wird. Mit <code>html/select</code> suchen wir nun wieder per CSS-Selektor nach der Stelle die uns interessiert -- in dem Fall die URL zum Buch-Cover. Zurück bekommen wir eine Clojure-Datenstruktur des selektierten. Daraus müssen wir dann nur noch die für uns relevanten Stellen heraussuchen. Ziemlich mächtig also.</p>
<p>Leider ist Enlive <a href="https://github.com/cgrand/enlive/commits/master">hoffnungslos veraltet</a>. Das muss im Clojure-Land nichts Schlechtes bedeuten, manche Bibliotheken sind einfach gut so, wie sie sind und müssen nicht mehr gepflegt werden. Leider scheint das Projekt keinen Maintainer mehr zu haben (Bug-Tracker hat noch offene Issues und die Pull Requests sind auch schon extrem lange offen). Das ist sehr schade. Mit <a href="https://github.com/cgrand/enliven">Enliven (Enlive Next)</a> wurde versucht, den genialen Gedanken weiterzutreiben, allerdings ist auch dieses Projekt seit 2014 nicht mehr gepflegt worden. Dennoch ist Enlive benutzbar und der Ansatz ist meiner Meinung nach prima! Besonders für das Web Scraping eignet es sich hervorragend.</p>
<h2 id="hiccup">Hiccup</h2>
<p>Einen Schritt weiter geht Hiccup, in dem komplett auf das HTML Template verzichtet wird. Was? Genau, es gibt in Hiccup keine HTML Templates mehr. Das wirkt im ersten Moment befremdlich, doch reduziert es die Komplexität enorm. Hiccup ist <a href="https://weavejester.github.io/hiccup/">extrem einfach</a>. Die API besteht nur aus einer Hand voll Funktionen, die schnell zu erlernen sind. Unser Hello World Beispiel von oben sähe so aus:</p>
<pre><code>(hiccup.core/html [:div "Hallo Aaron"])
</code></pre>
<p>Das <code>html</code> Makro nimmt einen Vector entgegen und gibt HTML zurück. Hiccup ist also nur ein Generator, der aus einem Vector HTML erstellt. Auch hier können wir wieder die Power von Clojure nutzen und sind nicht auf eine Template-Sprache beschränkt. Das Übergeben von Variablen an die View ist somit auch hinfällig. Alles ist Clojure-Code. Unser Beispiel von oben:</p>
<pre><code>(hiccup.core/html
[:div#covers
(for [[link title] cover-urls]
[:div#cover-item
[:a {:href link}
[:img {:src link :title title :alt "LovelyBooks cover"}]]]))
</code></pre>
<p>Mehr ist es im Grunde nicht. Alles was man tun muss ist einen Vector zu erzeugen und in <code>hiccup.core/html</code> zu geben. Der Rest ist dann Aufgabe von Clojure bzw. <a href="https://www.lispcast.com/hiccup-tips">dem Programmierer</a>. Es gibt noch ein paar Hilfsfunktionen für Formulare und das sichere Encoding/Escaping von Inhalt. Alles andere ist dem Programmierer überlassen. Wer schon einmal mit <a href="https://github.com/omcljs/om">Om</a> gearbeitet hat, wird sich deshalb direkt zu Hause fühlen. Und das beste daran ist: Hiccup wird aktiv weiterentwickelt.</p>
<p>Während also die meisten Web-Frameworks noch mit klassischen Template Engines arbeiten, hat der lose komponentenorientierte Ansatz von Clojure ein paar sehr interessante Konzepte zutage gebracht. Im Vergleich zu Hiccup und Enlive wirken Template Engines wie Twig oder Selmer fast schon als wären sie aus einer dunklen Vergangenheit und heute nicht mehr relevant. Dennoch wird gerade Twig heute noch fleißig eingesetzt. Auch moderne Frameworks wie Vue.js setzen auf den Template-Gedanken mit Template-Sprache. Zeit für ein Umdenken?</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/258
2019-02-18T22:54:00+01:00
2019-02-18T22:54:00+01:00
Programmiersprachen: Pattern Matching & Destructing in eLisp
<p>In <a href="https://clojure.org/">Clojure</a> stößt man
unweigerlich auf <a href="https://clojure.org/guides/destructuring">Destructing</a>, was man -- einmal
verstanden -- nicht mehr missen will. Dieses Sprachfeature macht
die Zuweisung von Daten in einer Datenstruktur in Variablen
sehr einfach. Dabei werden Muster mit Platzhaltern definiert, auf
die dann die eigentliche Datenstruktur <q>aufgelegt</q> wird. Hört sich
kompliziert an, ist es aber nicht.</p>
<p>Folgenden Code kennt jeder Programmierer (PHP):</p>
<pre><code>function doSomething(options={}) {
$name = isset(options['name']) ? options['name'] : false;
$age = isset(options['age']) ? options['age'] : false;
...
}
</code></pre>
<p>Aus der Datenstruktur <code>$options</code> werden die Werte für die Variablen <code>$name</code> und <code>$age</code> ausgelesen, um dann anschließend damit zu arbeiten. In jeder Programmiersprache, die Variablen unterstützt, schreibt man zwangsläufig diesen Boilercode. Das Beispiel in Clojure:</p>
<pre><code>(let [name (:name options)
age (:age options)]
...)
</code></pre>
<p>Mit Destructing lässt sich dies nun viel einfacher abbilden:</p>
<pre><code>(let [[:name :age] options]
...)
</code></pre>
<p>Es wird die Datenstruktur definiert und <q>Platzhalter</q> mit Namen festgelegt, auf die dann die Daten matchen. Daher wird dieser Mechanismus auch Pattern Matching genannt. Die Datenstruktur mit den Platzhaltern (also das Pattern) kann in Clojure beliebig komplex sein. Das macht den Code nicht nur einfacher zu lesen, sondern auch noch robuster gegen ungewünschten Input. Passt die Eingabe nicht auf das Muster, bricht der Code direkt an dieser Stelle ab.</p>
<p>Emacs/eLisp bietet mit dem Paket <a href="https://repo.or.cz/w/emacs.git/blob/HEAD:/lisp/emacs-lisp/pcase.el">pcase.el</a> auch die Möglichkeit an, Pattern Matching zu verwenden. (Ist das nicht toll, in Lisp kann man sich Sprachfeatures einfach dazubauen ohne den Kern zu verändern!) Die Funktion <code>pcase</code> wird automatisch geladen, man benötigt also kein <code>(require ...)</code>.</p>
<p>Hier ein Beispiel in eLisp mit <code>pcase-let</code>:</p>
<pre><code>(pcase-let ((`(,name ,age) options))
...)
</code></pre>
<p>Das Muster definiert wieder Platzhalter (Bindings), die dann im Body verfügbar sind. pcase kann aber noch mehr. Ein anderes Beispiel:</p>
<pre><code>(pcase-let ((`(,_ 2 ,c) (list 1 2 3)))
...)
</code></pre>
<p>Mit <code>,_</code> wird das erste Element ignoriert (muss aber da sein), das zweite Element der Liste muss eine <code>2</code> sein und das dritte Element wird an <code>c</code> gebunden. Auch lassen sich Abhängigkeiten innerhalb des Musters definieren. Hier bspw:</p>
<pre><code>(pcase-let ((`(,x ,x) (list 1 1)))
...)
</code></pre>
<p>Hier muss das erste und das zweite Element der Liste gleich sein (der Inhalt ist nicht relevant, muss aber gleich sein).</p>
<p>Interessant wird es mit der <code>pcase</code>-Funktion, die sich wie ein normales <code>case</code> verhält, nur mit dem Pattern Matching Mechanismus. Das macht den Code extrem übersichtlich und einfach verständlich:</p>
<pre><code>(pcase (list 1 2 2)
(`(,a ,b) (+ a b))
(`(,a 2 ,b) 5))
</code></pre>
<p>Hat die Liste zwei Elemente, werden sie addiert. Hat die Liste an zweiter Stelle eine 2, dann wird 5 ausgegeben.</p>
<p>Clojure hat das Spezialsymbol <code>&</code>, mit dem man auch dynamische Datenstrukturen handhaben kann. Ein <q>Catch all</q> für den Rest sozusagen. In pcase ist das ein Built In Feature der Sprache. (Schon wieder diese Eleganz von Lisp :).</p>
<pre><code>(pcase (list 1 2 3)
(`(,a . ,rest) (apply '+ rest)))
</code></pre>
<p>Das Muster wird einfach als ein Paar definiert. <code>car</code> wird an <code>a</code> gebunden und <code>cdr</code> (also der ganze Rest, egal wie groß dieser sein mag) an <code>rest</code>. In <code>rest</code> steckt dann logischerweise die Liste <code>(2 3)</code>, die dann mit <code>apply</code> auf der Funktion <code>+</code> angewendet wird und zu 5 evaluiert.</p>
<p>Was im Hintergrund passiert, lässt sich gut mit <code>pp-macroexpand-expression</code> nachvollziehen:</p>
<pre><code>(let*
((#:val
(list 1 2 3)))
(if
(consp #:val)
(let* ((#:x10 (car #:val))
(#:x11 (cdr #:val)))
(let ((rest #:x11)
(a #:x10))
(apply '+ rest)))
nil))
</code></pre>
<p>Das Makro übernimmt für uns den ganzen lästigen Boilerplate Code mit der Zuweisung der Variablen und stellt gleichzeitig sicher, dass die Struktur die ist die wir erwarten.</p>
<p>Zu <code>pcase</code> gibt es noch einiges mehr, wie Guards oder Prädikate. Es lohnt sich also mal in <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Pattern-matching-case-statement.html">das Manual</a> zu schauen. Wer mit eLisp nichts anfangen kann, <a href="https://en.wikibooks.org/wiki/Haskell/Pattern_matching">sollte</a> <a href="https://clojure.org/guides/destructuring">dennoch</a> <a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node252.html">einmal</a> <a href="http://tony.pitluga.com/2011/08/08/destructuring-with-ruby.html">in</a> <a href="https://golang.org/ref/spec#Assignments">seiner</a> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Lieblingssprache</a> <a href="https://secure.php.net/manual/en/function.list.php#example-6266">suchen</a>, ob sie Destructing bzw. Pattern Matching Features bereithält.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/267
2018-06-10T00:11:00+02:00
2018-06-10T00:11:00+02:00
Das 18650 Experiment: Teil 3/4: Sichere 18650 Akku-Packs bauen
<p>Sind die gebrauchten Zellen für gut befunden und bereit für den Wiedereinsatz, können wir daraus einen neuen Akkupack bauen. Nicht jeder Akkupack ist gleich und muss auf die eigenen Bedürfnisse abgestimmt werden. Bei der Montage des Packs sind ebenfalls ein paar Grundregeln zu beachten, damit nicht schon beim Bau alles in Flammen aufgeht. Zur weiteren Absicherung werden wir ein Battery-Management-System (BMS) verwenden, um den Akkupack noch besser abzusichern und das anschließende Laden zu vereinfachen.</p>
<p><img src="/images/18650_assembly4.jpg" alt="1"></p>
<h2 id="parallelschaltung-reihenschaltung">Parallelschaltung, Reihenschaltung</h2>
<p>Akkupacks gibt es in vielen verschiedenen Größen und Konfigurationen. Eine USB-Powerbank unterscheidet sich grundlegend nur in der Konfiguration von einem Akkupack in einem Tesla. Mit Konfiguration ist hier die Anordnung der Zellen gemeint. Ausschlaggebend sind zwei Werte:</p>
<ul>
<li><p><strong>Volt</strong>: Wie viel Volt/Spannung (U) soll der Akkupack am Ende haben? Dies ist natürlich keine exakte Angabe, da eine Zelle unterschiedliche Spannungen haben kann, je nach dem wie weit sie geladen ist. Dazu kann man auch keine Zellen zerschneiden. So muss immer eine Vielzahl von ~3.7V gewählt werden. (Wie die korrekte Spannung erreicht werden kann, werden wir in einem weiteren Artikel sehen) Um die Voltzahl zu erhöhen, müssen wir mehrere Zellen <strong>in Reihe schalten</strong>.</p></li>
<li><p><strong>Ampere</strong>: Wie viel Ampere/Strom (I) soll der Akkupack am Ende bieten, und welche Last können wir anschließen? Auch diese Angabe können wir mit unserem neuen Akkupack nicht exakt angeben. Zwar haben wir nun von jeder Zelle die Kapazität ermittelt, doch werden sich die einzelnen Zellen mit der Zeit verschlechtern und aneinander angleichen. Auch wissen wir von keiner Zelle das C-Rating. Dennoch können wir versuchen, unserem Wunschziel so nah wie möglich zu kommen, in dem wir die richtigen Zellen wählen und sie <strong>parallel schalten</strong>.</p></li>
</ul>
<p>Ich werde für meinen Anwendungsfall einen Akkupack mit 12V und einer Kapazität von ca. 15A/h benötigen. Die 12V geben mir schon vor, dass ich <strong>drei Zellen in Reihe</strong> schalten muss um <code>3x3.7V = 11.1V</code> zu erhalten. Da meine Zellen alle ca. 2000mA/h haben, werde ich <code>15000mA/2000mA = 7.5*3 Zellen</code> brauchen. Da wir aber eine Vielzahl von drei benötigen, werde ich 21 Zellen verbauen. Meine Konfiguration ist also ein <strong>3S7P</strong> Akkupack, drei in Reihe (S), sieben parallel (P).</p>
<p><img src="/images/pack.png" alt="1"></p>
<h2 id="zellen-ausw-hlen">Zellen auswählen</h2>
<p>Nun geht es an das Auswählen der Zellen. Hier kommt es darauf an, dass wir möglichst gleichartige Zellen verwenden -- sowohl vom Hersteller, als auch von der Kapazität. Da wir die Zellen miteinander verbinden, werden sich die Zellen mit der Zeit aneinander angleichen. Zellen mit kleiner Kapazität ziehen also Zellen mit großer Kapazität in Mitleidenschaft. Auch muss bei stark unterschiedlichen Zellen das BMS mehr leisten (Ballancing). Es lohnt sich also, Zellen mit ähnlicher Kapazität zu wählen.</p>
<p>Die große Frage ist nun: Wie ordnen wir die Zellen am sinnvollsten an? Dafür gibt es ein praktisches Tool namens <a href="http://repackr.com/">repackr</a>. Dieses Tool berechnet die optimale Anordnung der zur Verfügung stehenden Zellen. Oben werden alle Kapazitäten der ausgewählten Zellen eingetragen. Darunter geben wir unsere Konfiguration an (3S7P).</p>
<p>Zur Montage des Akkupacks empfiehlt es sich, entsprechende Halterungen zu verwenden. Dies bekommt man sehr günstig bei AliExpress (Suchbegriff: <q>18650 battery holder</q>) oder man druckt sie <a href="https://www.thingiverse.com/thing:1452396">sich selbst</a>.</p>
<p><img src="/images/18650_assembly0.jpg" alt="1"></p>
<p>Am einfachsten geht es, wenn man sich eine Zelle vornimmt, die Kapazität im Ergebnis von repackr.com sucht und dann an die entsprechende Position einsteckt. Auf der Webseite kann man dann die Zelle als <q>in Verwendung</q> markieren (auf die Zahl klicken), um so einen besseren Überblick zu bekommen. Ganz wichtig ist hier noch die Polarität, deshalb sollte man sich zuvor Gedanken machen, wie man die Zellen verbinden möchte.</p>
<h2 id="l-ten-vs-schwei-en">Löten vs. schweißen</h2>
<p>Beim <a href="/erd">Ausbau der Zellen</a> ist sicher schon aufgefallen, dass die Zellen meist mit einem Band (ein Nickel-Band oder auch Bus bar genannt) punktuell verschweißt waren. Dieses Verfahren (Punktschweißen) stellt sicher, dass die Zellen nicht zu heiß werden und eine solide Verbindung hergestellt wird. Da ein solches Punktschweißgerät recht teuer ist, kann man die Zellen auch miteinander verlöten.</p>
<p>Natürlich ist dies nicht die beste Option und muss vorsichtig gemacht werden. Verlöten hat allerdings den Vorteil, dass jede Zelle zusätzlich mit einem Sicherungsdraht versehen werden kann. Alte Zellen können (bzpw. bei Tiefenentladung) den Innenwiderstand auf null sinken lassen. Der komplette Strom fließt dann durch die defekte Zelle. Dies ist bei einer einzelnen Zelle nicht schlimm, doch da sie im Verbund direkt möglicherweise viele voll aufgeladene Zellen kurz schließt, kann es schnell zu einem Brand führen. Deshalb muss in einem solchen Fall die defekte Zelle vom restlichen Verbund herausgelöst werden.</p>
<p>Das funktioniert am Besten mit einem Sicherungsdraht (engl. fuse wire). Dies ist ein Draht, der in Feinsicherungen verwendet wird und bei einer definierten Anzahl an Ampere schmilzt. Ich habe hier verzinnten Kupferdraht in 0.315mm Dicke verwendet, der bei ca. 9 Ampere schmilzt.</p>
<p><img src="/images/18650_assembly2.jpg" alt="1"></p>
<p>Beim eigentlichen Verlöten sollte man natürlich darauf achten, die Zellen nicht länger als 2-3 Sekunden mit der Lötkolbenspitze zu bearbeiten. Mit einer breiten Spitze auf 380-390 Grad geht das mit etwas Übung sehr gut.</p>
<h2 id="schutzschaltungen-bms">Schutzschaltungen, BMS</h2>
<p>Nun können wir theoretisch den Akkupack schon in Betrieb nehmen. Mit einem Multimeter kann die Spannung an den beiden Polen kontrolliert werden. Hier sollten in meinem Fall 11-12 Volt anliegen.</p>
<p>Würden wir den Akkupack so verwenden, würden wir bald auf ein paar Probleme stoßen: Wir würden die Zellen tiefenentladen und dadurch kaputtmachen. Zudem haben wir keinerlei Möglichkeit, den Akkupack zu laden, da wir die drei in Reihe geschalteten Zellenverbunde beim Laden überwachen müssen (wie im <a href="/hmx">vorherigen Artikel beschrieben</a>).</p>
<p>Beide Probleme löst ein sog. <a href="https://de.wikipedia.org/wiki/Batteriemanagementsystem">Battery Management System (BMS)</a>. Dieses stellt sicher, dass die Zellen im definierten Bereich betrieben werden (keine Tiefenentladung oder Überspannung) und Überwacht das Laden (Ballancing). Hierbei wird die Spannung an den jeweiligen Punkten zwischen den Zellen gemessen und entsprechend den Strom und die Spannung geregelt.</p>
<p>Ich habe das BMS-Board HX-3S-FL25A verwendet, das preislich bei ca. 2 EUR lag. Beim Kauf ist lediglich darauf zu achten, dass es richtig dimensioniert ist (max. Ampere) und auf die eigene Akkupack-Konfiguration passt (in meinem Fall 3S, 25A max).</p>
<p><img src="/images/18650_assembly3.jpg" alt="1"></p>
<p>Ist alles verlötet, ist der Akkupack einsatzbereit. Ich habe ihn in meinem Fall noch mit etwas Kapton-Klebeband umwickelt, damit die Kabel nicht unabsichtlich abgerissen werden können.</p>
<p><em>Was nun noch fehlt ist eine Schaltung, die eine definierte Spannung liefert, egal wie voll der Akkupack ist. Dies wird Thema des nächsten Artikels sein.</em></p>
<p><strong>Nachtrag 19.10.2020:</strong> Fehler in der Berechnung der Gesamtkapazität behoben, danke an Christian!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/268
2018-06-05T09:44:00+02:00
2018-06-05T09:44:00+02:00
Elektronik: Hardware for "Objects In Space"
<p><a href="http://objectsgame.com/">Objects in Space</a> is an unique approach to combine flight simulators with an epic space game. You are the captain of a space ship, ready to explore a vast universe. Like any good simulator, there are a lot of buttons to press, so the creators of that game (two to be precise) built a interface to attach real hardware to the game. So you can build your own command center and control the game with it. How awesome is that?</p>
<p><img src="/images/commandcenter.png" alt="ois commander">
The game itself is currently in closed beta, but will soon become early access on <a href="https://store.steampowered.com/app/824070/Objects_in_Space/">STEAM</a> (approx. end of June). I have the honor to test the game in the current beta state and play a little with the hardware interface. Because the game is not released yet, there is not much information on how to do that. There is <a href="http://objectsgame.com/the-controllers/arduino-tutorial/">an article</a> and a <a href="http://objectsgame.com/the-controllers/ois-serial-data-protocol/">protocol specification</a>, but both are outdated and some critical information is misleading or is missing completely. So I will explain the minimal approach to create a little hardware device which will show the status for the main engine.</p>
<h2 id="the-interface">The Interface</h2>
<p>To activate the serial interface, an additional config setting is neede. Open the <code>objectsinspace.cfg</code> file and add the following:</p>
<pre><code>hardware=true
ignorecom12=true
</code></pre>
<p>The first option activates the serial console, the second skips COM1 and COM2 if your PC uses these ports as hardware ports.</p>
<p>After you start the game and open up a savegame, the game will try to open up a serial connection over a serial port. This serial port can be opened up by a microcontroller connected to a PC. Small development boards like an Arduino include a serial to USB chip to access the microcontroller from within the Arduino IDE (flash firmware, receive debug output, ...). This way, the game communicates with the hardware.</p>
<p><img src="/images/ois_talk.gif" alt="communication"></p>
<p>To start communicate with the game, the hardware device first need to fulfill a handshake. After that, the hardware specify what information is needed and what actions the hardware can offer. After that configuration, the hardware can switch to active mode and start reading events from the serial stream (the engine is burning, the ship is docked, ...) and send actions to it (start engine, undock ship, fire, ...). This information can be used to control the actual hardware (turn on LEDs, show numbers on displays, spin a fan, ...)</p>
<ol>
<li><p><strong>Handshake</strong>: After the serial connection is established, the hardware device need to initiate a handshake by sending the magic number <code>451\n</code> to the game. The game answers with a <code>452\n</code> when everything is sane. Repeat this handshake until the game responds with <code>452\n</code>.</p></li>
<li><p><strong>Initialization</strong>: Next, we need to configure the needed information. This happens in the form <code><TYPE>=<COMMAND>,<IDENTIFIER>\n</code>. As an example: <code>NIB=MAIN_ENGINE_BURNING,1\n</code> will receive the current status of the boolean action MAIN_ENGINE_BURNING periodically and on change, under the identifier 1. A more detailed description of the different types is shown in the <a href="http://objectsgame.com/the-controllers/ois-serial-data-protocol/">OiS Serial Data Protocol page</a>. A complete list of commands can be shown <a href="http://forum.objectsgame.com:88/t/ois-arduino-code-during-beta/911/16">in a post from user zebra</a>.</p></li>
<li><p><strong>Switch to active mode</strong>: To end the configuration and switch to active mode, you need to send a <code>ACT\n</code> to the game. After this point, the game sends status updates when the status is changed ingame and periodically every couple of seconds. The receiving data is in the form <code><IDENTIFIER>=<VALUE></code>. In our example above, this would be <code>1=1</code> if the main engine is burning and <code>1=0</code> if the main engine is offline.</p></li>
</ol>
<h2 id="programming-the-arduino">Programming the Arduino</h2>
<p><img src="/images/ois_burn.gif" alt="communication"></p>
<p>To build the firmware, we just need to implement the steps above. First, we open the serial connection on <code>9600</code> baud. After that, we do the handshake. If this is successful, we configure one event and switch to active mode. In the main loop we read a string from serial and process the received message. If we get an <code>1=1</code>, we switch on a LED, otherwise, we turn it off.</p>
<pre><code>void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
// Handshake
Serial.write("451\n");
String serverResponse = Serial.readString();
// Synchronisation
if (serverResponse == "452\n") {
Serial.print("NIB=MAIN_ENGINE_BURNING,1\n");
Serial.print("ACT\n");
}
}
void loop() {
// Handle one response from the server/game
String serverMsg = Serial.readString();
if (serverMsg == "1=1\n") {
digitalWrite(LED_BUILTIN, 1);
} else if (serverMsg == "1=0\n") {
digitalWrite(LED_BUILTIN, 0);
}
}
</code></pre>
<p>Connect your <a href="https://de.aliexpress.com/wholesale?SearchText=arduino+nano">Arduino</a> to your PC and use the <a href="https://www.arduino.cc/en/Main/Software">Arduino IDE</a> to flash the code into the microcontroller. After that, the onboard LED will light up when the main engine is burning.</p>
<p>Sure, this is just a minimal example. To get this working reliably, there is some additional work to do like handling broken messages or lost connections. But you can get an idea how this is supposed to work. If you need more pins than the Arduino can offer, you can use a cheap <a href="http://www.ti.com/lit/ds/symlink/sn74hc595.pdf">shift register like the SNx4HC595</a> to attach even more stuff. For a cheap alternative to the Arduino, you can also use an <a href="/pdc">ESP8266</a>.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/265
2018-05-03T23:40:00+02:00
2018-05-03T23:40:00+02:00
Das 18650 Experiment: Teil 2/4: 18650 Zellen recyceln
<p>Einer gebrauchten 18650 Zelle sieht man leider nicht direkt an, wie viel Kapazität sie noch hat. Der aufgedruckte Wert entspricht höchstwahrscheinlich nicht mehr der Realität. Deshalb braucht es einen Vorgang, um die Restkapazität zu ermitteln. Mit diesem Wert lässt sich dann entscheiden, ob der Zelle ein zweites Leben geschenkt wird oder entsorgt werden sollte. Der Vorgang selbst kann man tatsächlich als Recycling bezeichnen, denn die Zelle durchläuft einen kompletten Lade- und Entladezyklus.</p>
<h2 id="restkapazit-t-ermitteln">Restkapazität ermitteln</h2>
<p>Um die Performance einer Zelle zu ermitteln, müssen wir sie zuerst bis zur Ladeschlussspannung laden. Danach wird geprüft, ob die Zelle die Spannung halten kann. Defekte oder sehr alte Zellen scheiden hier schon aus. Anschließend wird die Zelle bis zur Entladeschlussspannung entladen und dabei gemessen, wie viel Strom entnommen wurde. So haben wir die noch vorhandene Kapazität ermittelt.</p>
<p>Dieser Prozess ist relativ Zeitaufwändig, so dass es sich lohnt, diesen so weit wie möglich zu parallelisieren. Zudem es gibt ein paar Details zu beachten, die nun genauer beschrieben werden.</p>
<p><img src="/images/18650_stack.png" alt="stack"></p>
<h2 id="spannung-messen">Spannung messen</h2>
<p>Als erstes können wir alle Zellen aussortieren, die weniger als <strong>2.8 Volt</strong> vorweisen (warum, wurde im <a href="/erd">letzten Artikel</a> genauer beschrieben). Hierzu kann man ein normales Multimeter verwenden. Es gibt Möglichkeiten, solche Zellen wieder zu aktivieren, allerdings haben sie bereits Schaden genommen und verschlechtern später unseren Akkupack mehr als dass sie Nutzen bringen. </p>
<p>Wer dennoch das Experiment durchführen will, kann die schlechten Zellen aussortieren, versuchen sie <a href="https://www.youtube.com/watch?v=YYGQDMprx5I">wieder zu reaktivieren</a> und daraus dann einen separaten Akkupack bauen. Es macht allerdings keinen Sinn, sie mit guten Zellen zu mischen. Zellen, die parallel geschaltet sind, gleichen ihre Spannung im Laufe der Zeit aneinander an. Somit werden die guten Zellen auch in Mitleidenschaft gezogen, wenn die schlechten zu schnell an Spannung verlieren. Selbes gilt für die Kapazität; Hier müssen wir später darauf achten, dass Zellen mit ähnlicher Kapazität parallel geschaltet werden.</p>
<h2 id="laden-bis-zur-ladeschlussspannung">Laden bis zur Ladeschlussspannung</h2>
<p>Die guten Zellen können wir nun bis zur Ladeschlussspannung laden. Dies ist bei LiIo/LiPo-Akkus nicht ganz trivial, da sie mit einem speziellen Verfahren (CC/CV, auch IU-Ladeverfahren genannt) geladen werden müssen. Im <a href="https://www.fveaa.org/forums/index.php?topic=1302.0">folgenden Schaubild</a> ist ein Ladevorgang einer LiIo-Zelle beschrieben, die ca. 1600mA fasst und mit 1A geladen werden kann.</p>
<p><img src="/images/cccv.jpg" alt="cccv"></p>
<p>Zu Beginn wird die Zelle mit konstanten 1A Strom (Constant Current) geladen, bis die Zelle eine Spannung von 4.2 Volt erreicht. Ab diesem Zeitpunkt wird die Spannung bei 4.2V gehalten (Constant Voltage) und der Strom wird so lange reduziert, bis er gegen 0mA geht. Ist dieser Punkt erreicht, ist die Zelle voll geladen.</p>
<p>Besitzt man ein Multimeter und ein Netzteil, welches den Strom und die Spannung frei wählen und begrenzen kann, lässt sich der Ladevorgang damit realisieren. Dies ist allerdings mit viel manueller Arbeit verbunden, da konstant die Spannung geprüft werden muss, um den Punkt zu ermitteln, ab wann von CC auf CV gewechselt werden muss.</p>
<p>Bequemer geht es natürlich mit einem Ladegerät, das den CC/CV-Modus unterstützt. Ich habe das Graupner Ultramat 16S Ladegerät verwendet. Sehr beliebt ist auch das SkyRC IMAX B6. Natürlich funktioniert dies auch mit jedem beliebigen anderen Ladegerät, welches einen LiPo-Modus (CC/CV) und einen Ballancer-Anschluss (dazu gleich mehr) besitzt. </p>
<p><img src="/images/18650_charge.png" alt="charge contraption"></p>
<p>Zum Anschluss an das Ladegerät habe ich mir eine kleine Halterung gebaut, die 6 Zellen in Reihe gleichzeitig laden kann. Auf dem Bild zu sehen sind kleine weiße Kabel, die am Pluspol jeder Zelle angeschlossen wurden. Dies sind die Ballancer-Kabel. Da jede Zelle unterschiedlich ist, muss jede separat überwacht werden. Dies verhindert, dass einzelne Zellen zu viel Spannung abbekommen. Die <q>schwächste</q> Zelle bestimmt demnach den maximalen Strom. Es lohnt sich also, Zellen mit ähnlicher Spannung und Bauart gleichzeitig zu laden, dies beschleunigt den Ladevorgang etwas.</p>
<p>So, nun die Frage: Was muss ich am Ladegerät einstellen?</p>
<ul>
<li><p><strong>Ladestrom</strong>: Dafür wird das sog. C-Rating benötigt. Es gibt an, wie viel Ampere der Akku im Betrieb abgeben kann. Hat ein Akku 2400mA/h und ein C-Rating von 15C, kann er im Betrieb <code>2400mA*15C = 36A</code> abgeben. Für das sichere Laden einer 18650 Zelle wird meist ein C-Rating von 1C angegeben, also <code>2400mA*1C = 2.4A</code>. Die meisten können aber deutlich mehr. Ich habe meine Zellen zur Sicherheit alle mit <code>1A</code> geladen.</p></li>
<li><p><strong>Kapazität</strong>: Diese Angabe ist eigentlich <code>relativ egal</code>, da wir sie zum einen nicht wissen und zum anderen dies von Zelle zu Zelle unterschiedlich ist. Das Ladegerät schaltet ab, sobald alle Zellen voll sind. Hier kann also ein hoher Wert eingestellt werden.</p></li>
<li><p><strong>Ballanceranschluss</strong>: Werden mehrere Zellen gleichzeitig in Reihe geladen, sollte auf jeden Fall mit einem Ballancerkabel gearbeitet werden. Das Ladegerät erkennt automatisch, wenn das Ballancerkabel angeschlossen ist und zeigt die Spannung der einzelnen Zellen separat an. Es schlägt auch Alarm und verweigert den Start wenn eine Zelle eine zu niedrige Spannung hat. Sind Zellen bereits miteinander verbunden (lagen nebeneinander im Notebook-Akku), haben sie sich bereits angeglichen und können parallel geladen werden.</p></li>
<li><p><strong>Ladeschlussspannung</strong>: Bei den 18650 Zellen beträgt sie <code>4.2 Volt</code>.</p></li>
<li><p><strong>Akkutyp</strong>: Hier kann Lithium-Ionen (<code>LiIo</code>) (oder wenn diese Einstellung nicht verfügbar ist, Lithium-Polymer (LiPo)) ausgewählt werden.</p></li>
</ul>
<p>Ist alles eingestellt, kann der Ladevorgang gestartet werden. Dies dauert je nach Ladestrom, Menge und Akkus zwischen 1-3 Stunden.</p>
<h2 id="ist-die-zelle-stabil">Ist die Zelle stabil?</h2>
<p>Man bekommt so gut wie jede Zelle auf 4.2V, allerdings entscheidet sich nun, ob die Zelle die Spannung auch halten kann. Aus diesem Grund sollte man nach dem Laden die Spannung jeder Zelle mit einem Multimeter messen und auf die Zelle schreiben. Dies dient später zum Vergleich.</p>
<p>Nun lagert man die Zellen an einem sicheren Ort für 1-2 Wochen und misst dann erneut. Ist die Spannung um <strong>mehr als 10%</strong> gesunken, sind die Zellen höchstwahrscheinlich schon zu alt, um sie wiederzuverwenden.</p>
<h2 id="entladen-kapazit-t-messen">Entladen & Kapazität messen</h2>
<p>Alle Zellen, die es bis hier her geschafft haben, können nun zur eigentlichen Kapazitätsmessung herangezogen werden. Einige Ladegeräte haben eine Entladefunktion, die dafür genutzt werden kann. Leider kann immer nur eine Zelle zur gleichen Zeit entladen werden, so dass dieser Prozess langwierig wird. Ich habe mir deshalb bei Aliexpress drei <a href="https://de.aliexpress.com/wholesale?catId=52802&initiative_id=AS_20180503014624&SearchText=18650+capacity+tester">Kapazitätsmesser</a> gekauft. Dies ist im Grunde nichts anderes als ein primitives Messgerät mit einem Verbraucher. Als Verbraucher werden Hochlastwiederstände verwendet, welche die Energie in Wärme umwandeln.</p>
<p><img src="/images/18650_discharge_contraption.png" alt="discharge contraption"></p>
<p>Nach dem Entladen können die entnommenen mA/h vom Display abgelesen und ebenfalls auf der Zelle notiert werden. So haben wir die Qualität und die Restkapazität der Zellen ermittelt und können dann entscheiden, was mit ihnen geschehen soll. Die beiden Kennzahlen helfen bei dem Bau und der Errechnung der Gesamtkapazität größerer Akkupacks.</p>
<h2 id="lagerspannung">Lagerspannung</h2>
<p>Da die Zellen nun alle bis an das untere Limit entladen wurden, sollten sie in absehbarer Zeit wieder geladen werden, damit die Spannung nicht aufgrund des Alters der Zelle weiter abfällt und dann unter die kritische Grenze kommt. Viele Ladegeräte haben einen Modus, um einen Akku auf eine Lagerspannung zu bringen. Hierbei wird der Akku halb voll geladen, so dass er einen stabilen Zustand erhält, in dem er auch auf längere Zeit bleiben kann.</p>
<p><em>Die so recycelten 18650 Zellen lassen sich nun in vielen Geräten wiederverwenden. Besonders in Taschenlampen oder DIY Elektronikprojekten sind sie sehr beliebt. Natürlich kann man nun einen ganzen Verbund von Zellen bauen (Akku-Pack), der dann mehr mA/h liefert oder eine höhere Spannung hat. Dazu dann mehr im nächsten Artikel.</em></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/264
2018-04-24T21:40:00+02:00
2018-04-24T21:40:00+02:00
Das 18650 Experiment: Teil 1/4: 18650 Zellen finden und bewerten
<p>Energiespeicher werden in unserer modernen Gesellschaft immer wichtiger. Es ist schon fast zur Selbstverständlichkeit geworden, dass Geräte ohne Kabel auskommen und selbst Energie aufnehmen und bei Bedarf abgeben können. Besonders beliebt in vielen Applikationen sind Lithium-Ionen- und Lithium-Polymer-Akkus; speziell die vom Typ 18650. Oft finden sich die 18650 Zellen in Geräten wie Laptop-Akkus, elektrischen Zahnbürsten oder e-Zigaretten. Selbst in Teslas sind diese verbaut. </p>
<p>In vielen Fällen lassen sie sich gut recyceln und somit nicht nur Geld sparen, sondern auch der Umwelt einen Gefallen tun. In der nun kommenden Serie von Artikeln werden wir alte Laptop-Akkus recyclen, daraus einen sicheren Energiespeicher bauen und diesen mit Hilfe von Solarenergie laden. So werden wir zum eigenen Energielieferant.</p>
<h2 id="lithium-ionen-akkus">Lithium-Ionen-Akkus</h2>
<p>Zuerst aber ein paar Dinge zum Grundverständnis. Die 18650 Zellen sind im Allgemeinen Lithium-Ionen-Akkus (sie gibt es auch noch in <a href="https://de.wikipedia.org/wiki/Lithium-Ionen-Akkumulator#Bauformen">anderen Bauformen</a>). Sie haben eine extrem hohe Energiedichte und können demnach viel Energie auf kleinem Raum speichern und abgeben. Deshalb sind sie seit den 90ern sehr beliebt in allerlei portablen Geräten. Sie haben zudem keinen Memory-Effekt, wie man ihn bei normalen AA-Zellen der älteren Generation kennt.</p>
<p>Bei diesen äußerst praktischen Vorteilen bleibt es aber leider nicht. Sie bringen auch ein paar Nachteile mit sich. Die Akkus sind gefährlicher in der Handhabung: Sie dürfen nur in einem definierten Temperaturbereich betrieben werden, dürfen auf keinen Fall tiefenentladen werden (Kurzschlussgefahr) und auch nicht überladen werden (Explosionsgefahr). Wenn die Akkus ausserhalb der Normparameter verwendet werden, können Sie explodieren und Feuer fangen. (Entsprechende Schutzvorrichtungen werden in einem folgenden Artikel beschrieben)</p>
<p>Auch das Laden der Akkus ist etwas aufwändiger, dazu aber auch später mehr. Nachfolgend eine Entladekurve eines <a href="https://www.elumeen.com/media/wysiwyg/produits/Elumeen_panasonic_NCR-18650B_en.pdf">Panasonic NCR18650B Akkus</a>. Hier lässt sich gut sehen, dass der Akku im normalen Betrieb mehr oder weniger Konstant zwischen 3,5 und 3,7 Volt liefert, egal wie groß der Verbraucher ist. Diese Charaktereigenschaft machen die Zellen so attraktiv.</p>
<p><img src="/images/18650_panasonic_ncr.png" alt="Entladekurve"></p>
<p>Gefährlich wird es dann bei ca. 2,5V, wenn die Spannung einbricht. Sinkt die Spannung unter 1 Volt, besteht die Gefahr, dass die Zelle intern einen Kurzschluss verursacht und dann sehr schnell sehr viel Energie freisetzt. Mit entsprechenden Schutzvorrichtungen lässt sich aber auch dies absichern.</p>
<h2 id="quellen">Quellen</h2>
<p>So, die erste Frage, die man sich vermutlich stellt: Wo bekomme ich alte Geräte zum Ausschlachten her? Hier gibt es eine ganze Menge an Quellen, denn alte Elektrogeräte werden häufiger weggeworfen als man denkt.</p>
<ul>
<li><p><strong>Recycling-Hof</strong>: Wer das Glück hat, einen Recycling-Hof in der Nähe zu haben, sollte hier mal einen Blick in die Elektroschrott-Ecke werfen. Hier finden sich oft alte Geräte. Besonders Interessant sind Laptops, elektrische Zahnbürsten und andere <q>cordless</q>-Gadgets wie tragbare Telefone oder Akkuschrauber. Eher uninteressant sind Smartphones und Kinderspielzeug.</p></li>
<li><p><strong>Flohmarkt</strong>: Auch hier finden sich oft alte Akku-Geräte. Auch wenn die Geräte an sich in schlechtem zustand sind, die Akkus sind meist noch zu gebrauchen.</p></li>
<li><p><strong>Arbeitskollegen, Freunde</strong>: So gut wie jeder hat auf dem Dachboden das ein oder andere alte Laptop oder einen defekten Akkuschrauber, für dessen fachgerechte Entsorgung man natürlich gerne behilflich ist :)</p></li>
<li><p><strong>Die Bucht</strong>: Auch auf eBay findet man unter dem Stichwort <q>Laptop/Akku Konvolut</q> ganze Akku-Sortimente. Hier lassen sich oft gute Schnäppchen machen.</p></li>
</ul>
<p>Natürlich kann man überall einen Volltreffer landen oder aber reihenweise defekte Zellen vorfinden. Dies lässt sich von außen leider nicht ermitteln. Hierzu müssen die Zellen ausgebaut und getestet werden.</p>
<h2 id="ffnen-und-ausschlachten">Öffnen und ausschlachten</h2>
<p>In der Regel muss zuerst das Plastikgehäuse aufgebrochen werden. Dies geht gut mit einem breiten Schraubendreher. Auch wenn man hierfür viel Kraft ausüben muss, ist es extrem wichtig, sehr sensibel vorzugehen. Zu schnell rutscht der Schraubendreher zu weit in das Gehäuse und beschädigt eine Zelle oder verursacht sogar einen Kurzschluss. Hierauf muss peinlichst geachtet werden.</p>
<p>Die Zellen sind meist mit doppelseitigem Klebeband im Gehäuse und untereinander verklebt. Mit etwas Fingerspitzengefühl lässt sich dann der Zellen-Verbund heraustrennen. Die Zellen selbst sind untereinander mit Metallplättchen verschweißt und hängen an einer Platine. Auf der Platine befindet sich meist eine Schaltung, die den Akku schützt und die abzugebende Spannung auf ein konstantes Niveau hält. Manchmal gibt es noch einen Temperaturfühler oder eine Füllstandsanzeige in Form von LEDs. Da die Platinen sehr speziell sind, lässt sich hier so gut wie nichts wiederverwenden und sind somit Restmüll. Die einzelnen Zellen können nun vorsichtig voneinander getrennt werden. Das geht ganz gut mit einer Kombizange. Natürlich immer darauf achten, keinen Kurzschluss zu verursachen.</p>
<iframe width="800" height="450" src="https://www.youtube-nocookie.com/embed/vO_OekoE3pg?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<h2 id="schnelltest-restspannung-messen">Schnelltest (Restspannung messen)</h2>
<p>Ob die Zelle zu gebrauchen ist oder gerade umsonst ausgebaut wurde, lässt sich schnell mit einem Multimeter prüfen. Da jede Zelle unterschiedlich ist, lässt sich hier keine exakte Aussage treffen, ab wann sie praktisch Müll ist. Allerdings verhalten sich die meisten 18650 Zellen ähnlich, wie in der <a href="http://www.candlepowerforums.com/vb/showthread.php?308451-18650-battery-test-with-capacity-curves-for-many-cells">folgenden Grafik</a> zu sehen.</p>
<p><img src="/images/18650_discharge.png" alt="18650 Discharge"></p>
<p>Hier wurden mehrere verschiedene Zellen mit 0.5A entladen. Wie man sieht, bricht die Spannung bei den meisten bei 3,4 Volt ein und sinkt rapide bei 2,8 Volt. Misst man also <strong>weniger als 2,8 Volt</strong> an der Zelle, kann man sie getrost entsorgen. Werte zwischen 3,6 und 3,7 Volt sind ideal.</p>
<h2 id="gef-hrliche-zellen">Gefährliche Zellen?</h2>
<p>Beim Öffnen des Gehäuses kann es natürlich vorkommen, dass man trotz aller Vorsicht Zellen beschädigt. In diesem Fall sollte man die Zelle direkt entsorgen. Ist nur das Plastik eingerissen, ist das nicht weiter tragisch. Wichtig ist, dass der Metallkörper vollständig intakt ist. Sind braune Stellen an den Kontakten, ist die Zelle verformt oder aufgebläht, sollte man die Finger davon lassen und direkt fachgerecht entsorgen. Ebenso für Zellen die weniger als 2,8 Volt vorweisen.</p>
<h2 id="kaputte-zellen-entsorgen">Kaputte Zellen entsorgen</h2>
<p>Aussortierte Zellen sollte man unter keinen Umständen in den Restmüll werfen. Liegen die Akkus auf irgend einer Deponie, ist es nur eine Frage der Zeit bis der Metallmantel anfängt zu rosten und Schwermetalle ins Grundwasser sickern. Zudem ist das Lithium extrem reaktionsfreudig wenn es mit großer Hitze oder Wasser in Berührung kommt. Also auf jeden Fall die Kontakte mit Klebeband abkleben und dann entsorgen. Das geht entweder wieder auf dem Recycling-Hof oder in einer der Sammelboxen in allen Läden die Batterien verkaufen (meistens im Kassenbereich). Durch das Recycling der Akkus können die wertvollen Materialien wieder zurückgewonnen werden.</p>
<h2 id="lagerung">Lagerung</h2>
<p>Idealerweise sollten die Zellen geschützt vor Wasser und all zu großer Hitze an einem <q>feuerfesten</q> Ort gelagert werden. Also nicht in einem Schuhkarton unter dem Bett oder neben der Badewanne. Ich habe sie in einem Plastik-Container im dunklen auf einem Steinboden gelagert.</p>
<p><em>Im nächsten Schritt muss die verbleibende Kapazität jeder Zelle ermittelt werden. Dies wird Thema des nächsten Artikels.</em></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/266
2018-02-19T09:00:00+01:00
2018-02-19T09:00:00+01:00
Elektronik: Das Ender-2 Kompendium
<p>Der 3D-Drucker <a href="http://www.creality3donline.com/product_detail.html?product_id=47">Ender-2 von CREALITY</a> ist ein kompakter und günstiger 3D-Drucker, der durchaus in der Liga der großen mitspielen kann. Beim Aufbau und der Kalibrierung sind allerdings ein paar Dinge zu beachten. Mit ein paar Modifikationen lässt sich dann noch etwas mehr Präzision herauskitzeln.</p>
<p>Ich habe mir vor einigen Jahren den <a href="http://reprap.org/wiki/Prusa_Mendel_(iteration_1)">Prusa Mendel (iteration 1)</a> aus Teilen aus dem Baumarkt selbst zusammengebaut. Ich war damit Monate beschäftigt, bis ich erste ansprechende Drucke erzielen konnte. Zu der Zeit gab es noch keine 3D-Drucker bei Amazon zu kaufen. In den letzten Jahren hat sich da allerdings einiges getan. Besonders der Anet A8 und der CR-10 haben im letzten Jahr den 3D-Druck für jeden erschwinglich und benutzbar gemacht. <a href="https://www.youtube.com/watch?v=IT59iwM6_kg">Das Video von JordsWoodShop</a> hat mich neugierig gemacht und der günstige Preis hat den Rest erledigt.</p>
<p><img src="images/ender2_full.jpg" alt="ender 2"></p>
<p>Der Drucker hat ein Druckvolumen von 150x150x200mm, nimmt normales 1,75mm Fillament entgegen und verspricht eine Genauigkeit von ±0,1mm. Der Stromverbrauch ist mit 100W angegeben. Das habe ich mit einem Strommessgerät nachgemessen und entspricht der Realität. Eine Stunde Drucken kostet somit ca. 3 Cent.</p>
<h2 id="bezugsquellen">Bezugsquellen</h2>
<p>Bestellt werden kann der 3D-Drucker entweder direkt aus China (Gearbest, ca. 140 EUR, 6-8 Wochen Lieferzeit), oder bei eBay (ca. 200 EUR, 4-6 Wochen Lieferzeit). Alles Weitere wie Fillament, Ersatzdüsen o.ä. lassen sich direkt in Deutschland bei Amazon oder eBay kaufen. Links zu den Händlern sind leicht über die Suchfunktion zu finden.</p>
<h2 id="aufbau-und-kalibrierung">Aufbau und Kalibrierung</h2>
<p>Der Aufbau gestaltet sich recht simpel und war in ca. 2 Stunden erledigt. Viele Teile sind schon zusammengebaut und müssen nur noch aneinandergeschraubt werden. Es gibt allerdings ein paar Kleinigkeiten, auf die man achten sollte, um sich die Kalibrierung später etwas zu vereinfachen und bessere Druckergebnisse zu erzielen.</p>
<p>Die <strong>X-Achse muss unbedingt parallel zum Befestigungsholm angeschraubt werden</strong>. Die Schrauben haben etwas spiel, so dass man hier gut justieren kann. Am besten klappt es, wenn man die Achse mit der Oberkante des Holms bündig macht. Wird sie schräg montiert, wird das Bed-Leveling zur Qual.</p>
<p>Der <strong>Steppermotor an der Z-Achse darf nicht schräg eingebaut werden</strong>. Zieht man die Schrauben fest an (was man tun sollte), verzieht sich der Motor leicht und macht die Z-Achse schwergängig. Die Kupplung am Steppermotor kompensiert zwar leichte Schrägstellungen, aber die Gefahr ist groß, dass sich doch mal was verklemmt und einzelne Steps ausgelassen werden. Hier habe ich ein paar Lagen Papier dazwischengeklemmt, so dass der Steppermotor komplett gerade eingebaut werden kann.</p>
<p><strong>Das beheizte Druckbett darf nicht wackeln</strong>. Dazu kann der mitgelieferte Schraubenschlüssel verwendet werden, um die Mutter oberhalb des linken Justierknopfs zu drehen. Die Mutter ist eine Art Cam Leaver, bei dem das Loch nicht in der Mitte angebracht ist sondern leicht versetzt. So lässt sich das Druckbett optimal fixieren. Es sollte sich allerdings noch leichtgängig bewegen.</p>
<h3 id="kalibrierung">Kalibrierung</h3>
<p>Die Kalibrierung des Druckbetts ist etwas anders als bei den meisten 3D-Druckern. Hier gibt es nur drei Punkte zur Justierung. Das Auto-Leveling Feature, welches sich über den Drucker aufrufen lässt, ist hier etwas umständlich, weil hier vier Punkte angefahren werden und man so die Prozedur zwei bis dreimal durchlaufen muss, um alles einigermaßen Plan einzustellen. Besser funktioniert folgende Technik:</p>
<ol>
<li>Auto-Home durchführen, danach Disable Steppers</li>
<li>Druckbett und Druckkopf von Hand genau über die linke Schraube positionieren</li>
<li>Linken Stellknopf so lange drehen, bis die Nozzle fast das Druckbett berührt. Das kann am besten mit einem Stück Papier getestet werden, das darunter platziert wird. Lässt sich das Papier gerade so mit einem leichten(!) Kratzen an der Nozzle herausziehen, ist es perfekt</li>
<li>Stellknopf hinten rechts ansteuern und dort auch wieder so lange am Stellknopf drehen, bis die richtige Höhe erreicht ist</li>
<li>Selbes mit dem Stellknopf vorne rechts</li>
<li>Zum Abschluss den Druckkopf in die Mitte des Druckbetts schieben und dort zur Verifikation ebenfalls mit einem Stück Papier den Abstand prüfen.</li>
</ol>
<p>Diese Prozedur lässt sich auch mit <a href="https://www.thingiverse.com/thing:2479290">einem praktischen Schnipsel G-code</a> erledigen. Dieser fährt die drei Punkte nacheinander an und druckt anschließend ein Viereck (3 Minuten Druckzeit), um die Haftung zu testen. Reißt man die fertigen Drucke nicht brutal vom Druckbett, muss man diese Prozedur nur ab und an durchführen.</p>
<h2 id="einrichten-der-software-cura">Einrichten der Software (Cura)</h2>
<p>Für die Transformation der STL-Dateien in G-code habe ich einige Jahre lang <a href="http://slic3r.org/">Slic3r</a> verwendet und gute Erfahrungen damit gemacht. Die Oberfläche ist simpel und schnell, bietet aber einiges an Einstellungsmöglichkeiten. Mit dem Ender-2 kam allerdings ein Druckprofil für <a href="https://ultimaker.com/en/products/ultimaker-cura-software">CURA</a> mit, so habe ich der Software eine Chance gegeben.</p>
<p>CURA ist vom UI sehr schwergängig (zumindest auf meinem PC), hat aber einige nützliche Features, die Slic3r nicht bietet. Aus dem Grunde habe ich mich dann für CURA entschieden. Ich habe Tests mit dem selben Objekt mit ähnlichen Einstellungen in CURA und Slic3r gemacht und subjektiv betrachtet hatte ich das Gefühl, dass CURA qualitativ hochwertigeren G-code erzeugt. Direkt vergleichen lässt sich das natürlich nicht, da beide Programme unterschiedliche Einstellungen bieten, doch hatte ich mit CURA bessere Druckergebnisse.</p>
<h3 id="die-wichtigsten-einstellungen">Die wichtigsten Einstellungen</h3>
<ul>
<li><strong>Layer Height</strong>: Die eigentliche Druckqualität. Je dünner die Schichtdicke ist, desto feiner wird die Oberfläche. Natürlich dauert dann der Druck entsprechend länger. Werte zwischen 0.1 und 0.3 sind je nach Anwendungsfall ausreichend.</li>
<li><strong>Infill Density</strong>: Große Flächen im Innern des zu druckenden Objekts brauchen unnötig Druckzeit und Material. Deshalb lässt sich die <q>Füllung</q> reduzieren. 40% ist meist ausreichend und behält dennoch genug Stabilität. Das Füllmuster ist hauptsächlich Geschmacksacke.</li>
<li><strong>Printing Temperature</strong> und <strong>Build Plate Temperature</strong>: Diese beiden Einstellungen sind entscheidend für gute Haftung und das Druckergebnis im Allgemeinen. Hier muss je nach Material selbst experimentiert werden. Für die meisten Materialien bietet CURA allerdings templates an, die man als Ausgangsbasis verwenden kann.</li>
<li><strong>Retraction</strong>: Um die Fadenbildung zu reduzieren, kann mit Retraction das Fillament beim Positionswechsel etwas zurückgezogen werden. Das verlängert die Druckzeit, macht aber ein besseres Druckergebnis. Mehr als 6mm sind unnötig, kommt aber auf das Material und die Druckgeschwindigkeit an.</li>
<li><strong>Print Speed</strong>: Hier muss man selbst experimentieren und hängt auch vom Material ab. Je höher die Druckgeschwindigkeit, desto schlechter das Druckergebnis. Hier gilt es den besten trade-off zwischen Druckgeschwindigkeit und Qualität zu finden. Es lohnt sich aber, die ersten 2 Layer langsamer zu drucken, um eine gute Haftung zu gewährleisten.</li>
<li><strong>Cooling</strong> und <strong>Fan Speed</strong>: Verbessert das Druckergebnis und erlaubt die Druckgeschwindigkeit bei kleineren Modellen zu erhöhen. Hängt aber wiederum vom Material ab.</li>
<li><strong>Support</strong>: Komplizierte Modelle erfordern Supportstrukturen, wenn die Überhänge größer als 45 Grad sind oder frei schwebende Elemente vorhanden sind. Allgemein sollte versucht werden, ohne Supportstrukturen auszukommen (durch geschicktes Drehen des Modells).</li>
<li><strong>Build Plate Adhesion Type</strong>: Es ist extrem wichtig, dass das zu druckende Objekt gut auf den Druckbett haftet. Bricht es nach 3 Stunden im Druck ab ist das extrem ärgerlich. Zu fest ist auch problematisch, denn dann bekommt man es nur mit viel Kraftaufwand wieder vom Druckbett. Hält es zwar, aber nicht richtig, gibt es hässliches <a href="https://capolight.wordpress.com/2010/07/25/warping-fundamentals/">Warping</a>. Die richtige Haftung am Druckbett ist also entscheidend. Als Daumenregel gilt: Ist das zu druckende Objekt klein, genügt ein <em>Skirt</em> (reinigt die Nozzle). Ist das Objekt groß, <em>Brim</em> verwenden (kann am Ende leicht weggeschnitten werden).</li>
</ul>
<p>Generell gilt: Um das experimentieren kommt man nicht drum herum. Es dauert ein paar Drucke, um die Settings für das verwendete Fillament zu optimieren. Hilfreich ist hier der <a href="http://reprap.org/wiki/Print_Troubleshooting_Pictorial_Guide">Troubleshooting Guild</a> vom RepRap Wiki.</p>
<h2 id="testdrucke">Testdrucke</h2>
<p>Sind alle Settings nach bestem Wissen gesezt, empfiehlt sich ein Testdruck. Recht bekannt ist <a href="https://www.thingiverse.com/thing:763622">Benchy</a>, ein kleines Schiffchen, das ein paar Schwierigkeiten für den 3D-Drucker bietet (überhänge, löcher, kleine Stege usw.). Hier kann man schnell die Schwachstellen des 3D-Druckers bzw. der gewählten Einstellungen sehen.</p>
<p>Einen weiteren Test kann man mit einem <a href="https://www.thingiverse.com/thing:477">40mm Würfel</a> machen. Dieser kann anschließend mit einem digitalen Messschieber nachgeprüft werden.</p>
<p>Die Toleranz kann mit <a href="https://www.thingiverse.com/thing:2719434">einem Bagger</a> getestet werden. Das Modell wird in einem Stück gedruckt, kann sich aber danach in alle Richtungen drehen, selbst der Baggerarm und die Räder lassen sich bewegen. Sind Gelenke <q>zusammengeklebt</q>, muss etwas an den Settings feinjustiert werden.</p>
<h2 id="optimierungen">Optimierungen</h2>
<p>Der 3D-Drucker selbst funktioniert schon sehr gut <q>Out of the box</q>. Ein paar Optimierungen machen aber das Arbeiten etwas einfacher und erhöhen die Druckqualität. Die folgenden Optimierungen lassen sich (bis auf die Antivibrationsmatte) alle direkt selbst drucken und benötigen keinerlei extra Hilfsmittel.</p>
<h3 id="vibrationen-verringern">Vibrationen verringern</h3>
<p>Der Drucker vibriert spürbar, wenn man über 40mm/s druckt. Dies ist besonders lästig, wenn man den 3D-Drucker auf dem Schreibtisch stehen hat. Um das zu unterbinden, gibt es Antivibrationsmatten für Waschmaschinen und Lautsprecher. Ich habe mir hier bei Amazon eine mit der Stärke von 5mm gekauft und zurechtgeschnitten.</p>
<p><img src="images/ender2_anti_vibration_mat.png" alt="ext"></p>
<p>Alternativ kann auch eine Korkplatte, Polystyrol oder ein anderes festes Dämmmaterial verwendet werden. Diese gibt es sehr günstig im Baumarkt.</p>
<h3 id="bautenzug-stabilisieren">Bautenzug stabilisieren</h3>
<p>Nach einigen Drucken ist mir der Silikon-Bautenzug aus der Muffe herausgerutscht. Deshalb habe ich mir ein paar <a href="https://www.thingiverse.com/thing:2743992">Clips für den Bautenzug</a> und zwei <a href="https://www.thingiverse.com/thing:936611">Abstandshalter für die Muffen</a> gedruckt.</p>
<p><img src="images/ender2_bowden_clip.png" alt="ext"></p>
<p>Die Abstandshalter verhindern, dass der Ring an der Muffe nach innen geschoben wird und somit der Bautenzug locker wird. Die Clips dienen der weiteren Stabilität. Kabelbinder gehen auch, sind aber nicht so flexibel.</p>
<p><img src="images/ender2_bowden_tube_clips.png" alt="ext"></p>
<h3 id="bessere-druckbett-justierung">Bessere Druckbett-Justierung</h3>
<p>Die drei Justierungspunkte am Druckbett sind mit Federn versehen, so dass immer genug Spannung besteht und nichts locker wird. Dies hat allerdings den Nachteil, dass die Feder ab und zu verrutscht und das sauber eingestelltes Druckbett wieder schräg steht. Abhilfe schaffen diese <a href="https://www.thingiverse.com/thing:2453023">Spring Cup Aligner</a>. Diese halten die Feder immer in der Mitte.</p>
<p><img src="images/ender2_level_knob.png" alt="ext"></p>
<p>Die mit dem 3D-Drucker ausgelieferten Knöpfe sind recht klein und erlauben keine wirklich präzise Einstellung. Mit den <a href="https://www.thingiverse.com/thing:2759467">Leveling Knobs</a>, welche man einfach über die bestehenden Knöpfe stülpt, geht das Bed-Leveling viel leichter von der Hand. Zu beachten ist, dass der Stellknopf hinten rechts mit dem Endstop kollidiert. Hierfür gibt es eine modifizierte Version (leveling_knob_back.stl), welche sich weiter hoch schieben lässt.</p>
<h3 id="bauteilk-hlung">Bauteilkühlung</h3>
<p>Für bessere Druckergebnisse ist aktive Bauteilkühlung sehr wichtig. Ein kleverer <a href="https://www.thingiverse.com/thing:2378558">Fan Duct</a> nutzt die Hotend-Kühlung für beides -- Hotend und Bauteilkühlung. Dabei wird der Lüfter schräg montiert, so dass die Luft auch nach unten strömen kann. Dieses System funktioniert sehr gut und der Umbau ist schnell gemacht. Zu beachten ist, dass sich die beiden Schrauben durchs Hotend mit der Zeit lösen. Hier hilft etwas Schraubensicherung.</p>
<p><img src="images/ender2_duct.png" alt="ext"></p>
<p>Um die Geschwindigkeit des Lüfters über CURA regeln zu können, muss der Lüfter auf dem Mainboard in die Buchse mit der Aufschrift <q>K-FAN</q> oder <q>LASER</q> umgesteckt werden.</p>
<h3 id="stabilisierung-der-z-achsen">Stabilisierung der Z-Achsen</h3>
<p>Die Gewindestange der Z-Achse ist oben nicht befestigt und wackelt entsprechend. Das ist nicht zwingend ein Problem, aber unschön. <a href="https://www.thingiverse.com/thing:2756824">Einen Halter für die Z-Achse</a> ist schnell gedruckt und installiert. Die Öffnung für die Gewindestange ist etwas größer gehalten, um ein Verkeilen auszuschließen.</p>
<p><img src="images/ender2_z_axis_stabilisator.png" alt="ext"></p>
<h3 id="fillament-f-hrung">Fillament-Führung</h3>
<p>Das Fillament wird direkt an der Z-Achse entlang geführt. Ölrückstände an der Gewindestange bleiben daher am Fillament haften, was nicht optimal ist. Abhilfe schafft <a href="https://www.thingiverse.com/thing:2579008">eine Führung</a>, die das Fillament an der Gewindestange vorbeiführt. Die 4mm-Version des Modells kann ein Stück vom Silikon-Bautenzug aufnehmen, so dass die Führung noch präziser funktioniert.</p>
<p><img src="images/ender2_fillament_spacer.png" alt="ext"></p>
<h3 id="beleuchtung">Beleuchtung</h3>
<p>Wer ein Stück LED-Stripe (12V) übrig hat, kann sich ganz unkompliziert das Druckbett beleuchten. Verbessert zwar das Druckergebnis nicht, doch erleichtert das Kalibrieren -- und man kann den Druckvorgang besser beobachten. <a href="https://www.thingiverse.com/thing:2700450">Die Halterung</a> muss zum Druck um 45 Grad gedreht werden, damit es auf das Druckbett passt. Angeschlossen werden kann es direkt auf dem Mainboard an einem der 12V Buchsen.</p>
<p><img src="images/ender2_light.png" alt="ext"></p>
<h2 id="weitere-verbesserungen">Weitere Verbesserungen</h2>
<p>Mit den aufgeführten Erweiterungen kann man schon einiges aus dem Ender-2 herausholen. Etwas aufwändiger sind die folgenden Erweiterungen, bringen aber ein paar entscheidende Vorteile.</p>
<h3 id="bessere-bauteilk-hlung">Bessere Bauteilkühlung</h3>
<p>Die oben beschriebene Bauteilkühlung hat einen Nachteil: Das Hotend wird nicht mehr optimal gekühlt, wenn die Bauteilkühlung auf 50% oder weniger geregelt wird. Zwei separate Lüfter lösen das Problem, einen Für das Hotend und einen für die regelbare Bauteilkühlung. Ein wirklich <a href="https://www.thingiverse.com/thing:2751027">bemerkenswertes Modell von Denny H.</a> kombiniert beides. Benötigt wird dazu ein weiterer 40mm 12V Lüfter und ein paar Schrauben. Ich habe hier einen aus einer alten Grafikkarte ausgebaut. Auch hier sollte wieder Schraubensicherung verwendet werden, da sich sonst die Befestigungsschrauben mit der Zeit lösen und das Hotend wackelt.</p>
<p>Ich bin sehr zufrieden mit dieser Lösung, da so das Hotend besser gekühlt wird und es eine bessere Sicht auf den Druckvorgang ermöglicht. Das ist speziell am Anfang des Drucks wichtig, falls noch Kunststoffreste am Hotend kleben. Aber auch beim Bed-Leveling ist eine bessere Sicht auf die Nozzle hilfreich.</p>
<h3 id="firmware-upgrade">Firmware upgrade</h3>
<p>Der 3D-Drucker wird mit einer leicht modifizierten Version der bekannten <a href="http://marlinfw.org/">Marlin-Firmware</a> ausgeliefert. Ich habe für mich noch keinen Grund gesehen, die Firmware zu aktualisieren. Wer dennoch die neuste Version der Marlin-Firmware verwenden will, um spezielle Features wie Auto-Bed-Leveling zu aktivieren, kann dies problemlos tun. Der ISP-Header ist auf dem Mainboard über Pinheader ausgeführt. Benötigt wird ein Atmel ISP-Programmer oder etwas vergleichbares, um die Firmware von der Arduino-IDE auf den AVR zu bringen.</p>
<h3 id="octoprint">Octoprint</h3>
<p>Ein extrem praktisches Tool ist <a href="https://octoprint.org/">OctoPrint</a>. Hiermit lässt sich der 3D-Drucker über ein Webinterface (oder über eine App) steuern und überwachen. So können lange/aufwändige drucke einfach tagsüber laufen und remote überwacht werden -- und im Fehlerfall abgebrochen werden.</p>
<p>Ich habe dafür einen <a href="http://www.orangepi.org/orangepizero/">Orange Pi Zero</a> (~6 EUR) mit <a href="https://www.armbian.com/orange-pi-zero/">armbian (Ubuntu Server)</a> verwendet.</p>
<p>Der Mini-PC benötigt 5V Betriebsspannung. Leider hat das beim Ender-2 mitgelieferte Netzteil nur einen 12V Ausgang. Mit einem <a href="http://www.st.com/resource/en/datasheet/CD00000449.pdf">L78S05</a> und zwei 100N Kondensatoren lässt sich aber die Spannung auf 5V senken. Der L78S05 sollte allerdings an einen Kühlkörper montiert werden, sonst wird er zu heiß. Die so gewonnene 5V-Spannung kann über die PIN-Leiste des Orange Pi Zeros eingespeist werden (Pin 1 und 2, siehe <a href="http://linux-sunxi.org/Xunlong_Orange_Pi_Zero#Expansion_Port">hier</a>).</p>
<p><img src="images/orangepizero.png" alt="orange pi zero"></p>
<p>Um initial das W-LAN einzurichten, muss entweder der Orange Pi Zero über ein Ethernet-Kabel ans Netz angeschlossen werden -- oder wie in meinem Fall die Konfiguration über die serielle Konsole vorgenommen werden. Ich habe einen <a href="http://dangerousprototypes.com/docs/Bus_Pirate">Bus Pirate</a> an den Header folgendermaßen angeschlossen.</p>
<pre><code>[TX][RX][GND] Orange Pi Zero
| | |
[RX][TX][GND] Bus Pirate
braun, rosa, schwarz
</code></pre>
<p>Konfiguration für den Bus Pirate:</p>
<pre><code>$ screen /dev/ttyUSB0 115200 8N1
m, 3, 9, 1, 1, 1, 2, (3), y
</code></pre>
<p>Danach den Orange Pi Zero booten. Das W-LAN kann mit dem Befehl <code>sudo nmtui</code> eingerichtet werden. Die Einstellungen bleiben bei einem Reboot bestehen.</p>
<p>Die Installation von OctoPrint selbst ist auf der <a href="https://github.com/foosel/OctoPrint">GitHub-Seite</a> gut beschrieben. Ein <a href="https://github.com/foosel/OctoPrint/blob/master/scripts/octoprint.init">init-script</a> ist ebenfalls vorhanden.</p>
<p>OctoPrint bietet die Möglichkeit, eine WebCam anzuschließen, um den Druckvorgang live zu beobachten und sogar Timelapse-Aufnahmen zu machen. Ich habe mit mehreren WebCams experimentiert, leider ist der Orange Pi Zero etwas zu schwach, um ein flüssiges Bild von der WebCam abzugreifen. Mit ein paar Abstrichen lässt sich dennoch die WebCam betreiben.</p>
<p>Um das WebCam-Bild abzugreifen, wird <a href="https://github.com/jacksonliam/mjpg-streamer">mjpg_streamer</a> verwendet. Die Installation dazu ist ebenfalls in der Readme des Projekts beschrieben. Folgende Startparameter haben mit meiner WebCam funktioniert:</p>
<pre><code>mjpg_streamer -i "input_uvc.so -r 640x480 -f 2 -q 50 -y" -o "output_http.so -w ./www"
</code></pre>
<p>Da der Orange Pi Zero nur einen USB-Anschluss auf eine Buchse geführt hat, muss entweder das USB-Kabel zum 3D-Drucker oder die WebCam über die PIN-Leiste angeschlossen werden.</p>
<pre><code>Schwarz -- GND
Rot -- 5V
Weiß -- Pin3 (USB-DM2)
Grün -- Pin4 (USB-DP2)
</code></pre>
<h2 id="fazit">Fazit</h2>
<p>Ich bin sehr beeindruckt, dass man für vergleichsweise wenig Geld einen so leistungsfähigen 3D-Drucker bekommt, bei dem man selbst alles verändern kann. Hier gibt es keine Komponente, die man nicht selbst modifizieren oder verbessern kann. So wünscht man sich jedes Produkt.</p>
<p>Aber auch der Ender-2 ist -- wie jeder 3D-Drucker unter 50.000 EUR -- nicht unfehlbar. Es ist keine Zaubermaschine, die alles erdenkliche materialisieren lässt. Es wird immer mal wieder Probleme geben, die man lösen muss. Hier darf man nicht aufgeben und sich immer sagen: Der Weg ist das Ziel. Es macht unglaublich viel Spaß, ein selbst modelliertes 3D-Modell auch noch selbst auszudrucken und am Ende physisch in den Händen zu halten.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/263
2017-09-13T21:32:00+02:00
2017-09-13T21:32:00+02:00
Game Jams: JS13KGames 2017 (Lost)
<p>Auch dieses Jahr habe ich wieder beim JS13KGames Contest mitgemacht. Das Thema war <q>Lost</q>. Da ich sehr gerne Point-and-Click Adventures spiele, habe ich mich dazu entschlossen ein Weltraumabenteuer nach dem Vorbild klassischer Adventures zu programmieren.</p>
<p>Als Framework habe ich dieses Mal <a href="https://straker.github.io/kontra/">kontra</a> verwendet, um mir das Handling mit Sprites und Animationen etwas zu vereinfachen. Allerdings habe ich von diesem Framework bis auf die animierten Sprites nichts verwendet. Da es keine Mausunterstützung und auch keine Textausgabe gibt, musste ich mir dies zuerst selbst bauen. Auch ein Entity-System habe ich mir selbst bauen müssen. Ich vermute, das Framework ist für diesen Typ Spiel nicht konzipiert worden.</p>
<p>Dennoch habe ich es irgend wie geschafft, den Code plus aller Assets bei 13Kb zu halten. Ich musste einige Abstriche bei den Grafiken machen und auch die Texte sind nicht so ausführlich wie ich sie gerne gehabt hätte. Gegen Ende musste ich noch einmal einiges löschen, um die magischen 13Kb nicht zu überschreiten. Die <q>Vollversion</q> kann <a href="https://data.datenhalter.de/js13kgames2017/index.html">hier</a> gespielt werden. Der Code liegt <a href="https://git.datenhalter.de/aaron/js13kgames-2017">hier</a>.</p>
<p><img src="/images/js13kgames2017.png" alt="image"></p>
<blockquote>
<p>Muri had no luck. He woke up in a malfunctioning stasis capsule on a old space ship. Nothing worked on this frickin trashcan. But he needed to fix it because there was nobody left on the ship who can do it. The lift had some special dislike against him, and tend to become it's own mind.</p>
</blockquote>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/262
2017-07-24T22:00:00+02:00
2017-07-24T22:00:00+02:00
Elektronik: Hørbert
<p>Audio-Geräte für Kinder gibt es so einige. Leider sind eigentlich <a href="https://www.google.de/search?q=kinder+radio&source=lnms&tbm=isch&sa=X&ved=0ahUKEwiGzrHExaLVAhVBPRQKHTx9CewQ_AUICygC&biw=1916&bih=1095">alle Schrott</a>, sehen hässlich aus, sind aus Plastik und können nicht modifiziert werden. Die meisten sind noch mit Kassetten oder CDs. Es gibt da zwar ein <a href="https://de-de.hoerbert.com/">anderes Gerät</a>, welches schon eher meinen Vorstellungen entspricht, preislich aber in einer ganz anderen Liga spielt. Deshalb habe ich für meinen Sohn selbst ein solches Gerät gebaut (welches aktuell jeden Tag im Einsatz ist).</p>
<p>Mein Hørbert kann neun MP3-Dateien per Tastendruck abspielen. Die MP3s befinden sich auf einer microSD Karte, welche mit jedem PC beschrieben werden kann. Die Lautstärke ist fest eingestellt. Als Akku dient eine kleine Powerbank, die mit einem Micro-USB Kabel wieder aufgeladen werden kann.</p>
<p><img src="/images/horbert1.jpg" alt="Horbert"></p>
<p>Die folgenden Komponenten habe ich dafür verwendet (keine Affiliate-Links). Kostenpunkt also 25-40 EUR, je nach dem, was man sowieso schon hat.</p>
<ul>
<li><a href="https://www.elv.de/mp3-sound-modul-msm-2-komplettbausatz.html">MP3 Sound-Modul MSM 2 von ELV</a> (20 EUR)</li>
<li><a href="https://www.elv.de/kleinlautsprecher-77-mm-8-ohm-0-5-1-w.html">8 Ω Lautsprecher</a> (1,50 EUR)</li>
<li><a href="https://www.elv.de/eingabetaster-0-1-a-32-v-dc-rund-blau.html">9 Eingabetaster in verschiedenen Farben</a> (9x0,60 EUR)</li>
<li><a href="https://www.pollin.de/p/usb-akkupack-exxter-105552-2600mah-schwarz-271763">Powerbank</a> (~4 EUR)</li>
<li><a href="https://www.pollin.de/p/microsdhc-card-4-gb-intenso-722137">4GB microSD Karte</a> (~6 EUR)</li>
<li><a href="https://de.aliexpress.com/item/40pcs-lot-10cm-2-54mm-1pin-feMale-to-feMale-jumper-wire-Dupont-cable/32485011284.html">Kabel</a> (~0,50 EUR)</li>
<li>Multiplex-Holz, oder eine schon fertige Box (~2 EUR)</li>
</ul>
<p>Als MP3-Player habe ich das MSM 2 Soundmodul von ELV verwendet. Bei Aliexpress gibt es ähnliche Boards für ~2 EUR. Das MSM 2 Board hat aber den entscheidenden Vorteil, dass die einzelnen MP3-Dateien per Tastendruck direkt abgespielt werden können. Das erspart den Bau einer Logik für die einzelnen Tasten. Das Board von ELV ist qualitativ hochwertig und die
<a href="https://files.elv.com/Assets/Produkte/9/928/92853/Downloads/92853_msm2_bda.pdf">Anleitung</a> hervorragend!</p>
<p><img src="/images/horbert4.jpg" alt="Horbert">
(Abbildung aus <a href="https://files.elv.com/Assets/Produkte/9/928/92853/Downloads/92853_msm2_bda.pdf">der Anleitung, Seite 4</a>)</p>
<p>Der Aufbau ist denkbar einfach: Alles nach Anleitung anstöpseln und mit 5V versorgen. Für die Stromversorgung habe ich ein altes USB-Kabel verwendet und mit der Powerbank verbunden. Interessant wurde es dann mit den Tastern. Diese sind so konzipiert, dass sie nicht einfach mit Sekundenkleber eingeklebt werden können; Das ganze Gehäuse des Tasters bewegt sich beim Betätigen. Deshalb habe ich die Taster auf kleine Stücke Lochraster gelötet und diese wiederum in eine Vertiefung im Holz geklebt. So kann sich das Gehäuse der Taster frei bewegen.</p>
<p><img src="/images/horbert2.jpg" alt="Horbert"></p>
<p>Einen Ein/Aus-Schalter habe ich ebenfalls nach außen geführt, so dass das Gerät auch abgeschaltet werden kann. Die Powerbank habe ich mit Heißkleber eingeklebt.</p>
<p><img src="/images/horbert3.jpg" alt="Horbert"></p>
<p>Das Gehäuse habe ich aus Multiplex gebaut, mit der Oberfräse überall abgerundet und mit Leinöl eingerieben. Das Kabelmanagement im Inneren könnte man noch etwas besser machen, doch bis jetzt tut es seinen Dienst.</p>
<p>Wie man sieht, lässt sich das Gerät für wenig Geld und mit minimalem handwerklichen Geschick einfach zusammenbauen.</p>
<p><img src="/images/horber5.jpg" alt="Horbert"></p>
<h2 id="verbesserungen">Verbesserungen</h2>
<p>Das aktuelle Setup funktioniert nun schon seit mehreren Monaten problemlos. Etwas störend ist noch das Aufladen der Powerbank und das Ändern der MP3s. Dazu muss die Rückblende abgeschraubt werden. Dies war so beabsichtigt, so dass kleine Kinderhände keine Knete in den USB-Slot drücken oder die microSD-Karte durch den Raum schießen können. Hier habe ich noch keine bessere Idee gefunden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/261
2017-07-22T22:47:00+02:00
2017-07-22T22:47:00+02:00
OAuth2 mit der NitrAPI
<p>Mit dem Launch der <a href="https://server.nitrado.net/deu/news2/view/nitrado-cloud-server-sind-ab-sofort-verfugbar/?f=home">Cloud Server</a> bei Nitrado ist die <a href="https://doc.nitrado.net/">NitrAPI</a> (die frei verfügbare API) noch interessanter geworden. Diese wird auch intern für die Webseite, die Smartphone-Apps und das Webinterface verwendet und bietet einige interessante Features an, die es in dieser Branche so noch nicht gibt. Die Autorisierung erfolgt über <a href="https://oauth.net/2/">OAuth2</a>, was leider für die meisten eine zu große Einstigeshürde darstellt. In dem folgenden Artikel will ich deshalb versuchen, OAuth2 (den <em>code</em> Workflow) anhand der NitrAPI zu erklären. Da OAuth2 ein Industrie-Standard ist, lässt sich das Folgende nicht nur auf die Schnittstelle von Nitrado anwenden.</p>
<p>OAuth2 bietet mehrere Autorisierungsmethoden an. Bei der NitrAPI wird die <a href="https://tools.ietf.org/html/rfc6749#section-4.1">authorization code Methode</a> verwendet. Die Grafik im <a href="https://tools.ietf.org/html/draft-ietf-oauth-native-apps-12#section-4.1"><q>OAuth2 Native Apps</q>-Draft</a> ist für unsere Fälle etwas einfacher zu verstehen:</p>
<pre><code class="bash">+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| User Device |
| |
| +--------------------------+ | (5) Authorization +---------------+
| | | | Code | |
| | Client App |---------------------->| Token |
| | |<----------------------| Endpoint |
| +--------------------------+ | (6) Access Token, | |
| | ^ | Refresh Token +---------------+
| | | |
| | | |
| | (1) | (4) |
| | Authorizat- | Authoriza- |
| | ion Request | tion Code |
| | | |
| | | |
| v | |
| +---------------------------+ | (2) Authorization +---------------+
| | | | Request | |
| | Browser |--------------------->| Authorization |
| | |<---------------------| Endpoint |
| +---------------------------+ | (3) Authorization | |
| | Code +---------------+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
</code></pre>
<p>Von der Client App (also unserer Anwendung) machen wir eine <em>Autorisierungsanfrage</em> (1) an den Browser. Wir Autorisieren uns über den Browser mit unseren Zugangsdaten (User, Passwort, 2-Factor-Auth, ...) (2). Zurück bekommen wir einen <em>Autorisierungscode</em> (3), den wir auf irgend einem Wege vom Browser zurück in unsere Anwendung bekommen müssen (4). Diesen <em>Autorisierungscode</em> können wir wiederum über die <em>NitrAPI</em> gegen einen <em>Access Token</em> eintauschen (5)(6). Diesen Workflow werden wir nun in Clojure implementieren. (Ist das Prinzip einmal verstanden, kann jede beliebige Programmiersprache verwendet werden)</p>
<p>Schauen wir uns den <a href="https://doc.nitrado.net/#api-OAuth_2-CreateAuthToken">entsprechenden Endpoint</a> in der NitrAPI etwas genauer an. Wir müssen also den Browser mit einer speziell präparierten URL aufrufen. Wir benötigen dafür allerdings eine <code>client_id</code> (und später auch ein <code>client_secret</code>). Um diese Infos zu erhalten, müssen wir zuvor unsere Client App bei Nitrado anlegen. Hierzu auf <a href="https://server.nitrado.net/">nitrado.net</a> einloggen (Account anlegen wenn noch nicht vorhanden) und dann unter <a href="https://server.nitrado.net/deu/users/index">Mein Account</a> -> <a href="https://server.nitrado.net/deu/developer/index">Entwickler-Portal</a> unter <q>Meine Anwendungen</q> auf das <q>+</q> klicken.</p>
<p><img src="/images/oauth2_nitrado1.png" alt="OAuth2 App anlegen"></p>
<p>Wichtig dabei ist die Weiterleitungs-URL. Da wir die Anwendung auf dem lokalen PC starten und nutzen, habe ich hier <code>http://localhost:9292/</code> verwendet. An diese Stelle wird später unser Autorisierungscode geschickt. Nach dem Speichern taucht die Anwendung in der Liste darunter auf. In den Eigenschaften lässt sich die <code>client_id</code> und das <code>client_secret</code> einsehen.</p>
<p>Die nächste Information, die wir noch benötigen ist der <code>scope</code> -- also der Bereich in dem der Access Token verwendet werden kann. Es ist ratsam, den Scope so eng wie möglich zu halten, um das Risiko eines Missbrauchs zu verringern. Angenommen, unsere Anwendung enthält eine kritische Sicherheitslücke, über die ein Fremder an den Access Token kommt. Wenn der angeforderte Access Token nur den Scope <code>user_info</code> besitzt, kann damit nicht auf die Services zugegriffen werden oder Bestellungen aufgegeben werden. Die möglichen Scopes sind <code>user_info</code>, <code>service</code>, <code>service_order</code> und <code>ssh_keys</code>.</p>
<p>Als letztes fehlt uns noch der <code>state</code>. Dies ist ein Sicherheitsfeature, damit wirklich nur unsere Anwendung die Autorisierung durchführen kann. Wir vergeben hier einen geheimen String (zufällig generiert), den nur wir kennen.</p>
<p>Rufen wir also den Browser mit unserer präparierten URL auf:</p>
<pre><code class="clojure">(defn oauth2 [client-id scopes]
(let [state (java.util.UUID/randomUUID)
(browse-url (str "https://oauth.nitrado.net/oauth/v2/auth"
"?redirect_uri=http://localhost:9292/"
"&client_id=" client-id
"&response_type=code"
"&scope=" (string/join " " scopes)
"&state=" state))
</code></pre>
<p>Die Funktion <code>browse-url</code> kommt aus dem Paket <code>clojure.java.browse</code> und öffnet den Standard-Browser mit der angegebenen URL.</p>
<pre><code class="clojure">(oauth2 "xxxclient_idxxx" ["user_info"])
</code></pre>
<p>Nach dem Aufruf werden wir auf die Login-Seite von Nitrado geleitet, welche Benutzername und Passwort fordert. Im nächsten Schritt (falls aktiviert), muss die <a href="https://de.wikipedia.org/wiki/Zwei-Faktor-Authentifizierung">2FA</a> Authentifizierung mit einem Pin oder einem <a href="https://www.yubico.com/products/yubikey-hardware/">YubiKey</a> getätigt werden. War dies erfolgreich, müssen wir die Berechtigung bestätigen. Hier sehen wir auch die angeforderten Scopes.</p>
<p><img src="/images/oauth2_nitrado2.png" alt="Scopes bestätigen"></p>
<p>Nach einem Klick auf <q>Erlauben</q> werden wir auf <code>http://localhost:9292/</code> weitergeleitet -- was natürlich zu einem nicht vorhandenen Webserver führt. Schaut man sich die URL genauer an, sieht man dort, dass hier ein <code>code</code> und unser zuvor angegebener <code>state</code> enthalten ist. Wir müssen also nichts weiter tun als einen kleinen Webserver starten, der diesen einen Request verarbeitet und die beiden Informationen ausließt. Dank der Clojure Lib <a href="http://www.http-kit.org/">HTTP Kit</a> ist das mit ein paar Zeilen erledigt.</p>
<pre><code class="clojure">(defn- handler [request]
{:status 200
:headers {"Content-Type" "text/html"}
:body "Hi there!"})
(org.httpkit.server/run-server handler {:port 9292})
</code></pre>
<p>Damit starten wir einen HTTP Server, der auf alle Anfragen mit dem Statuscode 200 und einem kleinen Gruß antwortet. Da wir den Webserver nur für diesen einen Request benötigen, können wir ihn danach direkt wieder schließen. Verbinden wir nun den Code für den Webserver und den Request-Handler mit unserer <code>oauth2</code> Funktion. Dies erfüllt Schritt (1) bis (4) des OAuth2 Workflows.</p>
<pre><code class="clojure">(defn- handler [request state client-id client-secret code]
(if (= (:uri request) "/")
(let [query (:query-string request)
[_ auth-code returned-state] (re-find #"code=(.+)&state=(.+)" query)]
(if (= (str state) returned-state)
(>!! code auth-code))))
{:status 200
:headers {"Content-Type" "text/html"}
:body "You can close the browser now."})
(defn oauth2 [client-id client-secret scopes]
(let [state (java.util.UUID/randomUUID)
code (chan)
request-handler #(handler % state client-id client-secret code)
http-server (org.httpkit.server/run-server request-handler {:port 9292})]
(browse-url (str "https://oauth.nitrado.net/oauth/v2/auth"
"?redirect_uri=http://localhost:9595/"
"&client_id=" client-id
"&response_type=code"
"&scope=" (string/join " " scopes)
"&state=" state))
(let [auth-code (<!! code)]
(http-server :timeout 500)
auth-code)))
</code></pre>
<p>Da unsere <code>oauth2</code> Funktion so lange blockieren soll, bis wir den auth-code erhalten haben, aber der ganze Vorgang asynchron läuft, müssen wir uns ein paar Tricks bedienen. In der <code>oauth2</code> Funktion öffnen wir einen neuen Channel mit <code>(chan)</code>, den wir anschließend (zusammen mit dem <code>state</code>, der <code>client-id</code> und dem <code>client-secret</code>) an den Request handler übergeben. Somit haben wir im Request-Handler <code>handler</code> die Möglichkeit, den auth-code an die <code>oauth2</code> Funktion zu übergeben. In der <code>oauth2</code> Funktion selbst warten wir so lange auf den <code>code</code>, bis er vom <code>handler</code> abgelegt wird. Ist das der Fall, können wir den <code>http-server</code> schließen und den <code>auth-code</code> zurückgeben. (<code>run-server</code> gibt eine Funktion zurück, die den HTTP-Server wieder schließt.)</p>
<p>Im <code>handler</code> holen wir uns <code>code</code> und <code>state</code> aus dem Request, prüfen ob der <code>state</code> unserem gespeicherten state entspricht und schieben dann den <code>auth-code</code> in den Channel. Das <code>if</code> zu Beginn dient übrigens dazu, dass wir nur Requests verarbeiten, die für uns relevant sind. Einige Browser machen noch zusätzliche Requests auf <code>/favicon.ico</code> o.ä. Diese wollen wir natürlich nicht verarbeiten.</p>
<p>Nun fehlen uns nur noch die Schritte (5) und (6), also der Austausch vom auth code mit einem Access Token. Die NitrAPI hat hierfür einen <a href="https://doc.nitrado.net/#api-OAuth_2-CreateAccessToken">Endpunkt</a>. Für den POST-Request können wir wieder die HTTP Kit Bibliothek verwenden. Da dieser Vorgang ebenfalls asynchron ist und in unseren Workflow integriert werden muss, müssen wir zudem ein paar Modifikationen an unserem bisherigen Code machen:</p>
<p>In der <code>oauth2</code> Funktion sind wir nun nicht mehr am <code>code</code> interessiert, sondern am <code>token</code>. Deshalb öffnen wir hier einen Channel für den <code>token</code>, und übergeben diesen an den <code>handler</code>. Dieser wiederum verarbeitet den den Request, steckt aber den code nicht in den Channel, sondern ruft die <code>access-token</code> Funktion auf. Diese übernimmt den Austausch von code und token und steckt anschließend den Access Token in den <code>token</code> Channel.</p>
<p>Hier der komplette Code (42 Zeilen), inklusive Namespace und Requires:</p>
<pre><code class="clojure">(ns clj-nitrapi.auth
(:require
[clojure.java.browse :refer [browse-url]]
[clojure.core.async :refer [>!! <!! chan]]
[clojure.string :as string]
[clojure.data.json :as json]
[org.httpkit.client :refer [post]]
[org.httpkit.server :refer [run-server]])
(:import
(java.util UUID)))
(defn- access-token [code client-id client-secret token]
(post "https://oauth.nitrado.net/oauth/v2/token"
{:query-params
{:client_id client-id
:client_secret client-secret
:grant_type "authorization_code"
:code code}}
(fn [{:keys [body]}]
(>!! token (json/read-str body :key-fn keyword)))))
(defn- handler [request state client-id client-secret token]
(if (= (:uri request) "/")
(let [query (:query-string request)
[_ code returned-state] (re-find #"code=(.+)&state=(.+)" query)]
(if (= (str state) returned-state)
(access-token code client-id client-secret token))))
{:status 200
:headers {"Content-Type" "text/html"}
:body "You can close the browser now."})
(defn oauth2 [client-id client-secret scopes]
(let [state (UUID/randomUUID)
token (chan)
request-handler #(handler % state client-id client-secret token)
http-server (run-server request-handler {:port 9292})]
(browse-url (str "https://oauth.nitrado.net/oauth/v2/auth"
"?redirect_uri=http://localhost:9595/"
"&client_id=" client-id
"&response_type=code"
"&scope=" (string/join " " scopes)
"&state=" state))
(let [token-data (<!! token)]
(http-server :timeout 500)
token-data)))
</code></pre>
<p>Zusammen mit dem Access Token erhalten wir auch einen Refresh Token. Diesen können wir gegen einen neuen Access Token eintauschen, ohne dass wir uns erneut authentifizieren müssen. Hierfür können wir den selben Endpunkt aus Schritt (5) und (6) nutzen. Nur geben wir hier nicht den <code>code</code> mit, sondern den <code>refresh-token</code>. Die Implementierung ist trivial:</p>
<pre><code class="clojure">(defn refresh [refresh-token client-id client-secret]
(let [resp (chan)]
(post "https://auth.nitrado.net/oauth/v2/token"
{:query-params
{:client_id client-id
:client_secret client-secret
:grant_type "refresh_token"
:refresh_token refresh-token}}
(fn [{:keys [body]}]
(>!! resp (json/read-str body :key-fn keyword))))
(<!! resp)))
</code></pre>
<p>Mit dem Access Token können wir nun auf die komplette NitrAPI zugreifen und uns unsere eigene Nitrado Webseite bauen, die Funktionen vom Nitrado Webinterface ins eigene Tool integrieren oder kleine Scripte schreiben, die Dinge automatisiert. Mein Kollege <a href="https://bitowl.de/">Stefan</a> hat für die <a href="https://play.google.com/store/apps/details?id=com.nitrado.nitradoservermanager&hl=de">Android-App</a> bereits eine <a href="https://search.maven.org/#search%7Cga%7C1%7Ca%3A%22nitrapi%22">Java-Lib</a> gebaut, die wir problemlos in Clojure verwenden können. Mehr dazu evtl. in einem folgenden Artikel.</p>
<p>Natürlich spricht nichts dagegen, den OAuth2 code Workflow in einer anderen Sprache zu implementieren. Der Vorgang bleibt der gleiche. Befindet man sich in einer asynchronen, eventbasierten Umgebung wie JavaScript oder PHP, ist die Implementierung noch einfacher.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/260
2017-05-23T23:00:00+02:00
2017-05-23T23:00:00+02:00
Projekte: Getting better every day
<p>Vor einiger Zeit habe ich das Buch <a href="https://www.lovelybooks.de/autor/Josh-Waitzkin/The-Art-of-Learning-856795179-w/">The Art of Learning</a> von <a href="https://de.wikipedia.org/wiki/Joshua_Waitzkin">Joshua Waitzkin</a> gelesen, was mich sehr fasziniert hat. Waitzkin ist Autor, Schauspieler, Weltmeister in der Kampfsportart Taijiquan und mit 16 war er schon Internationaler Meister im Schach. Das hat mir einmal mehr gezeigt, dass jeder gut in etwas werden kann, wenn man nur will.</p>
<p><img src="/images/tree.jpg" alt="Trees"></p>
<p>Vor ein paar Monaten habe ich <strong>einen Versuch</strong> gestartet, mit dem ich dies zeigen wollte: Ich bin nicht sehr gut im Zeichnen/Malen, aber ich wollte besser werden. Deshalb habe ich in den letzten ~5 Monaten <strong>100 Bäume gemalt</strong>. (<a href="https://www.youtube.com/watch?v=1JjEi3xdjMk">Niclas Huschenbeth</a> hat vor kurzem übrigens ein ähnliches Experiment gestartet).</p>
<p><img src="/images/trees.jpg" alt="Trees"></p>
<p>Angefangen habe ich mit Textmarker, Filzstift und Kugelschreiber. Als Papier habe ich normales Druckerpapier verwendet. Das hat eine Zeit lang gut ausgereicht und hat die Angst genommen, einen <q>falschen Strich</q> zu zeichnen. Dann bekam ich ein paar Copic Marker geschenkt und die Technik änderte sich. Ab Baum 70 habe ich dann mit Aquarellfarben gearbeitet.</p>
<p>Beim Malen habe ich einige Beobachtungen gemacht, dir mir ein paar Dinge wieder vor Augen geführt haben. Vermutlich lässt sich das Folgende auch in ganz anderen Bereichen wiederfinden.</p>
<h3 id="komfortzone">Komfortzone</h3>
<p>Jedes Mal, wenn ich etwas neues ausprobiert habe, ein neues Papier verwendet habe oder den Stil geändert habe, fühlte es sich anfangs <q>unbehaglich</q> an. Die Komfortzone des Vertrauten zu verlassen und etwas neues auszuprobieren ist nicht so leicht, weil es anfangs immer schlecht aussieht. Doch nach ein paar Tagen lernt man aus seinen Fehlern und verfeinert die Technik -- bis man sich wieder <q>zu vertraut</q> damit fühlt und wieder etwas ändern sollte. Beim Malen sieht man förmlich, wie man dabei besser wird. Macht man nicht den Schritt, etwas neues auszuprobieren, bleibt man ewig auf dem gleichen Level.</p>
<h3 id="ressourcen">Ressourcen</h3>
<p>Fehlendes Werkzeug oder Material sind keine Ausrede und auch kein Hinderungsgrund. Ich habe Bäume in der Bahn gemalt, im Hotelzimmer (im Bad, da war besseres Licht), auf einem Kassenzettel und Papiertüten oder bei dm am Kindermaltisch mit Buntstifte. Zudem habe ich alle Bäume auf <a href="http://f0086.deviantart.com/">deviantart</a> und ein paar auf Instagram/Twitter gepostet, was den Druck etwas erhöht hat, regelmäßig einen Baum abzuliefern. Oftmals hat es mir sogar geholfen, etwas neues auszuprobieren, wenn ich mit einfachen Mitteln gemalt habe. Feinstes kaltgepresstes Aquarellpapier will man nicht versauen und man macht damit lieber keine Experimente.</p>
<p>Doch tat es der Motivation -- und auch der Technik -- keinen Abbruch, sich mit besseren Materialien und Werkzeugen zu belohnen :) So habe ich mir im Laufe der Zeit Stifte, Papier, Pinsel und den Aquarellkasten gekauft.</p>
<h3 id="den-eigenen-stil-finden">Den eigenen Stil finden</h3>
<p>Irgend wann kommt man an einen Punkt, an dem man merkt, dass man den eigenen Stil gefunden hat. Mit der Zeit sieht man einem Baum direkt an, dass ich ihn gemalt habe. Kleine Details, die ich immer so zeichne. Man darf aber nicht Stil mit der eigenen Komfortzone verwechseln. Bei fast jedem Baum habe ich Kleinigkeiten entdeckt, die mir nicht gefallen haben. Andere Dinge haben mir so gefallen wie sie sind. Diese habe ich auch nicht geändert.</p>
<p>Viele Künstler haben sich auf eine Stilrichtung und/oder Materialien spezialisiert. Sind sie nur deshalb gut, weil sie schlicht unzählige Bilder der gleichen Sorte gemalt haben? Oder weil sie ihren eigenen Stil perfektioniert haben? Vermutlich beides.</p>
<h3 id="pl-tzlich-berall-b-ume">Plötzlich überall Bäume</h3>
<p>Sobald man sich mit einem Thema näher beschäftigt, erhält man von überall Einflüsse. Ich habe plötzlich die (schlecht gezeichneten!) Bäume in Kinderbüchern realisiert, auf Plakaten, selbst auf der Toilettenpapier-Verpackung sind Bäume aufgedruckt. Auch in der Natur habe ich Bäume plötzlich ganz anders wahr genommen. Zugegeben, im Winter Bäume zu malen ist keine kluge Idee gewesen, doch konnte man so die Äste besser sehen.</p>
<iframe width="800" height="500" src="https://www.youtube.com/embed/3DWQa6vr758" frameborder="0" allowfullscreen></iframe>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/257
2017-01-23T20:25:00+01:00
2017-01-23T20:25:00+01:00
Unsortiert: Gefühlte Wahrheit
<p>Ich wollte hier eigentlich nicht politisch werden, doch ich finde es ist an der Zeit, gewissen Strömungen gegenzusteuern und Stellung zu beziehen. Abwarten und hoffen dass die Vernunft schon siegen wird ist zur schlechten Option geworden.</p>
<p>Warum wird es immer schwerer, die Wahrheit von der Lüge zu unterscheiden? Damit beschäftigen sich gerade viele. Der POTUS lügt dreist und dreckig die ganze Welt an und jeder der mit der Wahrheit dakommt, wird mit <q>Fake News</q> abgestempelt. Der Begriff <q>alternative Wahrheit</q> ist schon ein Absurdum sondersgleichen, welches man bisher nur aus Wurmloch-Theorien und SciFi-Filmen kennt -- jetzt aber aus dem Mund von Kellyanne Conway (der Beraterin des POTUS). Der Begriff <q>Lügenpresse</q> verhilft der ganz weit rechts Partei AfD zur Mobilisierung von Stimmen, das Bauchgefühl katapultiert die Briten aus der EU und <q>postfaktisch</q> wird zum <a href="http://gfds.de/wort-des-jahres-2016/">Wort des Jahres 2016</a>. Was soll dieser ganze Blödsinn eigentlich?</p>
<p>Die Wahrheit ist schon lange nicht mehr der Goldstandard. Es wird jetzt aus dem Bauch heraus entschieden. Wenn Jens auf Facebook schreibt: <q>Das fühlt sich irgend wie falsch an</q> hat das mehr Gewicht als die Fakten. Politik ist ein hochkomplexes Gebilde, bei dem man nicht aus dem Bauch raus auf Basis seiner eigenen kleinen Filterblase entscheidet. Jeder Aktion hat eine Unzahl an Implikationen als Konsequenz. Wir sind als Welt in den letzten paar Jahrzehnten immer weiter zusammengerückt und sind immer mehr miteinander vernetzt. Alles hängt zusammen. Wer hier nur nach sich selbst schaut, riskiert fatale Folgen für sich und auch die Anderen.</p>
<p>Vielen Menschen fällt es immer schwerer zwischen Fakten und Meinungen zu unterscheiden. Das sind zwei komplett unterschiedliche Dinge. Ein Fakt -- z.B. dass eine 6 eine 6 ist und keine 9 ist so und bleibt auch so. Jeder, der etwas anderes behauptet, ist im Unrecht. Macht jemand eine Aussage und behauptet anschließend er hätte das nicht gesagt, lügt. Period. Jeder ist für sein Handeln selbst verantwortlich. Ein (politischer) Diskurs ist etwas ganz anderes. Jeder darf eine eigene Meinung haben, das nennt man <a href="https://www.gesetze-im-internet.de/gg/art_5.html">Meinungsfreiheit</a> (die aber auch Ihre <a href="https://de.wikipedia.org/wiki/Meinungsfreiheit#Grenzen_.28Schranken.29_der_Meinungsfreiheit:_Art..C2.A05_Abs..C2.A02_GG">Grenzen</a> hat). Die eigene Meinung ist extrem wichtig, um einen Konsens zu finden, der für alle akzeptabel ist.</p>
<p><img src="/images/69truth.jpg" alt="69"></p>
<p>Jeder kennt wohl diese Zeichnung. Aber eigentlich suggeriert sie etwas ganz falsches. Es kommt nicht auf den Standpunkt an. Einer der beiden hat nicht recht, denn irgend jemand hat eine Zahl auf den Boden gemalt. Die frage ist nun, welche Zahl. Die beiden sollten sich umsehen und einen Schritt zurück machen; nachsehen ob es noch andere Zahlen gibt an denen man sich orientieren kann, oder jemanden Fragen der es weiß.</p>
<p>US-Senator Daniel Patrick Moynihan hat das ganz <a href="https://en.wikipedia.org/wiki/Daniel_Patrick_Moynihan#Quotes">treffend gesagt</a>:</p>
<blockquote>
<p><q>Everyone is entitled to his own opinion, but not to his own facts.</q></p>
</blockquote>
<p>Google, Facebook und Twitter stecken uns in unsere Filterblasen. <a href="/qth">Neuronale Netze</a> lernen unsere kleine Welt kennen; tüten uns ein und versehen uns mit Tags. Zeigen uns nur <q>relevantes</q>, so dass wir dem Datenstrom Herr werden. Alles was hier nicht rein passt, <q>fühlt sich falsch an</q> (<a href="http://www.srf.ch/sendungen/100-sekunden-wissen/echokammer-effekt">Echokammer-Effekt</a>). Die Vermischung von Fakten und Meinungen ist perfekt. Die Wahrheit <a href="http://www.spiegel.de/netzwelt/web/post-truth-politics-sascha-lobo-ueber-die-luege-in-der-politik-a-865523.html">wird zur Option</a>.</p>
<p>Die eigene Filterblase zu verlassen ist mühsam. Sich auf unbequeme Meinungen und Standpunkte anderer einzulassen auch. Um die Wahrheit zu <q>erarbeiten</q> braucht es das aber. Ich gehöre zur <a href="https://de.wikipedia.org/wiki/Generation_Y">Generation Y</a> -- ich bin also mit dem Internet aufgewachsen. Sprüche wie <q>If it is on the internet, it must be real</q>, <q>Pic or it didn't happen</q> oder <q>The cake is a lie</q> sind nicht nur Gags -- sie beschreiben eigentlich den Umgang mit der digitalen Welt. Informationen sollte man stets hinterfragen und belegen, bevor man sie für valide hält.</p>
<p>Es ist einfach, etwas zu glauben, das in das eigene Denkmuster passt und so aufbereitet ist, dass man es leicht konsumieren kann. Ein 30-Sekunden AfD-Promovideo über einen gewalttätigen Syrer in der Facebook-Timeline von potentiellen Wählern kann da schon viel bewirken. Schnell schließt man sich der Meinung an, ohne überhaupt darüber nachgedacht zu haben. Besonders dann, wenn es mantramäßig wiederholt wird. Ich habe manchmal Sorge, dass die heutige Jugend zu bequem geworden ist, sich Fakten zu <strong>erarbeiten</strong>.</p>
<p>Denn die Techniken der Meinungsbeeinflussung sind perfider als man denkt. Z.B. mit einer noch absurderen Geschichte von etwas viel wichtigerem ablenken. Nach der Amtseinführung von Trump haben vier Millionen(!) Menschen demonstriert. Als Antwort kommt die offensichtliche Lüge, dass bei seiner Inaugurationsfeier mehr Menschen da gewesen wären als bei der Amtseinführung von Obama. Die Geschichte war so einfach zu wiederlegen, dass sich das Internet empört darüber ausgelassen hat -- und die vier Millionen Demonstranten waren plötzlich ganz unwichtig. So eine Aktion kann Absicht sein. Die eigene Filterblase kann einem zum Verhängnis führen. Russland hat dafür ganze <a href="http://www.spiegel.de/netzwelt/netzpolitik/russische-trollfabrik-eine-insiderin-berichtet-a-1036139.html">Troll-Fabriken</a>. Man erkauft sich eine Meinung -- oder gleich einen Fakt.</p>
<p>Es bilden sich schnell drei Gruppen. Die einen, die sagen: <q>Das wird schon stimmen.</q>, selbst wenn die Fakten offenkundig dagegensprechen. Die anderen sagen: <q>Das ist doch vollkommen gelogen, hier die Beweise</q>. Und die dritte Gruppe kommt zum Schluss: <q>Man kann nicht sagen, wer jetzt recht hat, weil ja Aussage gegen Aussage steht.</q> Und schon sind Beweise oder die Wahrheit nicht mehr von Bedeutung.</p>
<p>Richtig schlimm wird es dann, wenn versucht wird, Fakten verschwinden zu lassen, oder wenn die Verifikation der Wahrheit unmöglich gemacht wird. Die unangenehme Presse wird ausgeladen oder <a href="https://www.youtube.com/watch?v=KY7ObB-Yvrc">gleich</a> <a href="https://www.youtube.com/watch?v=OH9NxrKOcic">rausgeschmissen</a>, wenn sie doch auftaucht.</p>
<p>Erschwerend dazu kann man viele Sachen nicht 100% selbst verifizieren, wenn man es nicht selbst mit eigenen Augen gesehen hat. Wie schlimm ist der Bürgerkrieg in Syrien wirklich? Kann Donald Trump wirklich <a href="https://www.youtube.com/watch?v=bd79UsXSLWg">nicht richtig lesen</a>? Wem also vertrauen? In <a href="https://www.lovelybooks.de/autor/George-Orwell/1984-60655376-w/">George Orwells 1984</a> wird daher das <q>Ministerium für Wahrheit</q> eingeführt. SPD und CDU diskutieren schon über einen Gesetzesentwurf für ein <a href="https://kurier.at/politik/ausland/deutschland-behoerde-gegen-fake-news-das-riecht-ja-nach-zensur/238.084.853">Abwehramt gegen Desinformation</a>.</p>
<p>Was also tun? Welcher Quelle kann ich noch vertrauen? Zu welchem Maße? Folgende (zugegebenermaßen eher technische) Dinge halte ich für essenziell und wichtig in Zusammenhang mit unserer heutigen Gesellschaft:</p>
<ul>
<li><p>Das <a href="https://archive.org">Internet Archive</a>: Das gigantische Archiv, das nichts vergisst. Hier lässt sich schnell nachprüfen, wie sich eine Website über die Zeit verändert hat. Hat hier jemand etwas verschwinden lassen oder umgebaut? Wurde die Website offline genommen? Was stand hier zuvor? Aber nicht nur Webseiten, auch Interviews, Bilder, Radio-Mitschnitte usw. sind dort <em>frei zugänglich</em> archiviert. Brewster Kahle hat einen sehr ausgeprägten Sinn für Public Domain und <a href="http://www.attn.com/stories/13238/the-internet-archive-is-moving-to-canada-due-to-trump-presidency">tut alles daran</a>, dass das Internet Archiv nicht untergraben oder gelöscht wird.</p></li>
<li><p>Julian Assange (und ehem. Daniel Domscheit-Berg) mit <a href="https://wikileaks.com/">Wikileaks</a>, Edward Snowden, Chelsea Manning und alle anderen <a href="https://en.wikipedia.org/wiki/List_of_whistleblowers">Whistleblower</a>: Wenn Fakten vertuscht oder verdreht werden und hinter versperrten Türen gehalten werden, schauen die allermeisten weg. Ein paar haben dennoch den Mut, damit an die Öffentlichkeit zu gehen -- mit dem Risiko, lebenslang ins Gefängnis zu kommen oder sich den Rest des Lebens zu verstecken.</p></li>
<li><p>Das Überwiki Wikipedia: Im Studium wurde uns stets eingetrichtert, dass die Wikipedia nicht so richtig zitierwürdig ist, eine Quelle zweiter Klasse. Im Vergleich zu Peer-Reviewed Papers hat das schon seine Berechtigung, doch Wikipedia hat den Anspruch, objektiv, belegbar und akkurat die Fakten festzuhalten. Dass das nicht immer klappt, wenn die ganze Welt daran mitwirken kann, ist klar. Doch die Tatsache, dass die ganze Welt mitarbeiten kann und es eine Änderungshistorie zu jedem Artikel gibt, bleibt nichts ungesehen. Sind Quellen nicht eindeutig, ungenau oder fehlen, dauert es nicht lange und der Artikel erhält einen Warnhinweis. Wird versucht, Unwahrheiten zu verbreiten, fällt das auf und wird korrigiert. Die Diskussionsseite enthält darüber hinaus meist einen kontroversen Austausch. Ich denke, das ist als Informationsquelle weit näher an der Wahrheit als ein Zitat aus einem Buch oder einer Website, was von einer Person geschrieben wurde. Natürlich darf man nicht alles von Wikipedia für bare Münze halten, man sollte sich stets von den Quellen überzeugen lassen.</p></li>
<li><p>Die Gosse des Internets (4chan, krautchan, Tor-Netzwerk, Darknets): Es gibt ein paar Orte im Internet, wo jeder tun und lassen kann was er will. Natürlich teile ich nicht die Meinung vieler auf diesen Plattformen und einiges ist gar verstörend, doch ich finde es wichtig, dass es solche unregulierten, nicht überwachten Orte gibt. Hier kann nichts zensiert werden.</p></li>
<li><p>Mesh-Netzwerke (Freifunk), Amateurfunk und BitTorrent: Steckt eine Regierung in Erklärungsnot oder wird von der Wahrheit bedrängt, wird systematisch die <a href="https://www.reddit.com/r/darknetplan/comments/5pjanc/oppressive_regime_has_cut_off_our_internet_3g/">Kommunikation unterbunden</a>. Informationen können nicht mehr transportiert werden. Das ist in unserer Gesellschaft ein großes Problem. Um das zu verhindern, sind solche Projekte meiner Meinung nach essenziell. Im Idealfall sind wir alle dezentral miteinander vernetzt. Ist ein Torrent mal im Umlauf, bricht nicht ein Server zusammen wenn er von zu vielen gleichzeitig heruntergeladen wird -- das Netz stärkt sich selbst. Ein Löschen ist unmöglich. Freifunk und Amateurfunk geht auch ohne Telekom und Vodafone.</p></li>
<li><p>OpenPGP, Bitcoin, <a href="https://www.ethereum.org/">Ethereum</a> und die Blockchain: Ähnlich wie Wikipedia, vergisst die Blockchain nichts. Die komplette Historie ist immer dabei und für alle einzusehen. Es ist technisch nahezu unmöglich, etwas zu fälschen. Das ist sicher auch ein Grund, warum Bitcoins so beliebt sind. Trust-Netzwerke wie OpenGPG schützen nicht vor Trollen/Unwahrheiten, aber bauen ein Vertrauensnetz auf. Jeder lebt in seiner Filterblase. Jeder hat in seinem Leben unterschiedliches erlebt, Erfahrungen mit unterschiedlichsten Menschen gemacht und sich dadurch sein eigenes Bild von der Welt geformt. Die Frage ist ja, was genau ist die Wahrheit? Sie scheint bei manchen Themen für jeden etwas anders zu sein. <q>Angela Merkel ist eine gute Kanzlerin</q> kann vermutlich von jedem anders interpretiert werden. Je nach dem, was man im Zusammenhang damit selbst erlebt hat -- aber auch was man hierzu gelesen hat und sich sagen lassen hat; und wie sehr man dieser Quelle <strong>traut</strong>. (Unvorstellbar aber wahr, die Nerds meiner Generation geht auf <a href="http://rhonda.deb.at/projects/gpg-party/gpg-party.de.html">Keysigning-Parties</a>, wo wir uns gegenseitig die Ausweise vorzeigen).</p></li>
</ul>
<p>Was wir in den letzten Jahren erleben, ist die Vermischung von Fakten und persönlichen Meinungen. Wir laufen aber Gefahr, dass wir jetzt alle nach Amerika und Russland schauen und mit dem Finger auf Putin, Trump und <q>die dummen Ammis</q> zeigen. Doch vor der Haustüre hängt das AfD-Plakat und die NPD fährt mit dem Megafon durch die Straßen und brüllt die Propaganda ins Land (selbst erlebt!). Informationen werden zur Waffe und ohne es überhaupt zu merken, haben wir auch den Totalitarismus vor der Türe. Aber Hauptsache die neue App ist auf dem iPhone. Das ist ja schließlich das, was zählt. Wir sind so mit uns selbst beschäftigt, dass wir garnicht mitbekommen, was um uns herum passiert. Ich glaube für meine Großeltern muss es noch viel schlimmer sein als für mich. Sie haben am eigenen Leib erfahen, wohin so etwas führen kann.</p>
<p>Es lohnt sich also meiner Meinung nach dafür zu kämpfen, dass Phrasen wie <q>America first</q> oder <q>wir deutschen und die Ausländer</q> keine Bedeutung mehr spielen. Am Ende stehen wir alle auf dem selben kleinen Dreck-Klumpen den wir Erde nennen, der im Vergleich zum Rest so <a href="https://www.youtube.com/watch?v=afFQlnx7PEY">unbedeutend winzig erscheint</a>. Für diesen sind wir <strong>alle verantwortlich</strong>.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/256
2016-09-05T21:30:00+02:00
2016-09-05T21:30:00+02:00
Privates: Was ich so mache ...
<p>Seit gut zwei Monaten ist es wieder etwas ruhiger hier geworden. Eine Sommerpause sozusagen. So mag es den Anschein haben -- doch es ist einiges passiert, das schlicht noch nicht den Weg an die Oberfläche geschafft hat. Aus diesem Grund kann ich zur Zeit nur ein paar Teaser liefern, was die nächsten Wochen und Monate (höchstwahrscheinlich) kommen könnte.</p>
<h2 id="n3rdpad">n3rdpad</h2>
<p>Zusammen mit <a href="http://klaute.de/">Kai</a> arbeite ich an einem Tastatur-Kompagnon. Einer Art großen Bruder des <a href="/pzx">Emacs Keypad</a>. Die Platine haben wir bereits fertig und auch schon zwei Prototypen gelötet. Die Firmware ist auch schon weit fortgeschritten. Gerade arbeite ich an einem 3D Model für ein Gehäuse, welches man einfach mit dem 3D-Drucken replizieren kann. Ein Konfigurationstool, welches ich in Go schreibe, ist auch in Arbeit.</p>
<h2 id="plant-checker">Plant-Checker</h2>
<p>Mit <a href="https://twitter.com/webtagebuch">Markus</a> hatte ich die Idee eines intelligenten Blumentopf-Überwachungs-Tools mit ein paar Extra-Features. Als Grundlage soll ein ESP8266 dienen, welcher mit einem 18650 Akku betrieben wird. Das Ziel ist es, das Gerät einen Monat (oder länger) ohne Akkuaufladung laufen zu lassen. Erreichen wollen wir das mit der <a href="http://www.espressif.com/sites/default/files/9b-esp8266-low_power_solutions_en_0.pdf">Deep Sleep Funktion</a> des ESP8266. Bisher habe ich einen Prototyp gebaut, mit dem ich die Temperatur kontinuierlich in eine InfluxDB logge und mit Grafana visualisiere. Aktuell optimiere ich die Firmware, um noch mehr aus dem Akku rauszuholen. Sobald der Aufbau 1 Monat mit einer Akku-Aufladung läuft, kann das eigentliche Projekt umgesetzt werden.</p>
<h2 id="diy-electic-skateboard">DIY Electic Skateboard</h2>
<p>Ein Arbeitskollege und ich haben schon länger den Wunsch eines elektrischen Longboards. Ein <a href="https://boostedboards.com/the-board/">Boosted Board</a> kostet ein Vermögen und fertige Kits sind auch nicht wirklich bezahlbar. Wir beschlossen, eines von Grund auf selbst zu bauen. Hierfür haben wir einige Wochen mit der Recherche und dem Beschaffen passender Komponenten verbracht. Das Projekt ist mittlerweile recht groß geworden, da wir natürlich eine solide Lösung haben wollen, auf die man sich auch verlassen kann. Aktuell sind wir dabei, ein paar kleine Probleme mit der Mechanik und der Stromversorgung zu lösen.</p>
<h2 id="quadcopter-1-5">Quadcopter #1.5</h2>
<p>Ich habe meinen alten <a href="http://flugwiese.de/2010/12/murkelcopter/">Murkelcopter</a> komplett zerlegt, da die Holzkonstruktion durch die vielen Abstürze leider nicht mehr zu retten war. Eine Aluminium-Konstruktion beherbergt nun die alten Motoren und ESCs. Den alten Flight controller (der aus einem Arduino und einer Wii Remote bestand) habe ich durch einen NAZE32 von Aliexpress ersetzt. Hier bin ich gerade dabei die Firmware zu justieren, so dass der Quadcopter mal länger als 20 Sekunden in der Luft bleibt.</p>
<h2 id="knx">KNX</h2>
<p>Anfang des Jahres habe ich die Elektrik unseres Hauses komplett umgebaut. Neuer Verteilerschrank, neue Zuleitung -- und nun auch ein intelligenter Bus (KNX). Meine Werkstatt -- die ich mir auch in den letzten Monaten eingerichtet habe -- wird bereits komplett über den KNX-Bus gesteuert. Hier fehlt es aber noch an ein paar weiteren Sensoren (Türe, Präsenz, Zeit, Wetter, Licht), um die Steuerung noch intelligenter zu machen.</p>
<h2 id="vim-buch">VIM Buch</h2>
<p>Ich nutze jetzt <q>Vim</q> schon über ein Jahrzehnt (mittlerweile Emacs + evil-mode). Der Umgang damit ist mir schon so vertraut, dass ich nicht mehr darüber nachdenke. Die Finger wissen, was zu tun ist. Auch habe ich zu Vim schon Vorträge und Workshops gehalten. Ich werde immer wieder gefragt, ob ich nicht <q>mal ne Einführung in Vim</q> geben kann. Daran arbeite ich nun schon seit einiger Zeit in Form eines kleinen (e?)-Buches. Umfang und Inhalt ist bisher noch sehr vage. Der Grundgedanke dabei ist, dass jeder Einsteiger sein eigenes Vim grundlegend konfigurieren, bedienen und selbst erweitern kann. Aktuell habe ich einige losgelöste Kapitel, die noch einen anständigen Rahmen brauchen.</p>
<h2 id="adventskalender-2016">Adventskalender 2016</h2>
<p>Zusammen mit <a href="https://feitel.indeedgeek.de/">Florian</a> habe ich vor einigen Jahren immer zur Weihnachtszeit einen <a href="/c/advent">Adventskalender</a> gemacht. Beim letzten haben wir uns wohl etwas übernommen. Um was es dabei geht wollen wir noch nicht verraten -- haben uns aber vorgenommen ihn dieses Jahr fertigzustellen.</p>
<h2 id="yenu">yenu</h2>
<p>Auch schon sehr lange auf meiner ToDo Liste steht <a href="https://github.com/f0086/yenu">dieses Projekt</a>. Dieses muss ich demnächst auch zuende führen, da ich es für private Zwecke brauche.</p>
<p>Diese Projekte laufen gerade alle parallel zu meinem täglichen Job, meiner Familie, Freunden und ein <q>paar</q> anderen Dingen. Also seit gespannt, irgend wann wird hier wieder etwas passieren, es ist nur eine Frage der Zeit :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/255
2016-07-13T20:06:00+02:00
2016-07-13T20:06:00+02:00
Software Engineering: Titel
<p>Dem (akademischen) Titel wird ganz unterschiedliche Bedeutung und Ruf zugesprochen, je nach dem welcher Titel und in welchem Umfeld bzw. Branche er Anwendung findet. Der Titel <em>Dr.</em> beispielsweise hat einen sehr erhabenen Ruf. In der Software/IT-Branche hingegen darf sich jeder selbst zum <em>Software Architect</em> ernennen. Warum ist das so?</p>
<p>In der Handwerks-Branche gibt es strikte Regeln. Nur ein Elektriker-Meister darf den Schaltschrank aufbauen und die Qualitätssicherung übernehmen; VDE-Vorschrift. Niemand würde auf die Idee kommen, sich selbst zum Meister zu ernennen und anfangen Panzersicherungen mit einem Topflappen einzusetzen. Das ist auch richtig so, sie haben dafür auch gearbeitet und gelernt.</p>
<p>Ärzte hängen stolz ihre Urkunden im Sprechzimmer auf und bestehen vehement auf ihr <em>Dr.</em> Präfix. Das ist auch ihr gutes Recht, sie haben dafür auch viel Geld und Zeit geopfert -- und schwer gearbeitet (sofern sie nicht den Ehrendoktor verliehen bekommen haben). Gibt sich dennoch jemand unrechtmäßig den Doktortitel, wird er/sie öffentlich an den Pranger gestellt.</p>
<p><img src="/images/titel2.png" alt="titel"></p>
<p>In der Software/IT-Branche sieht es etwas anders aus. Ich habe Software Engineering studiert und darf den Titel <em>Ingenieur</em> bzw. <em>B.Sc.</em> tragen. Dafür habe ich einiges an Strapazen auf mich genommen, um überhaupt studieren zu dürfen. Während andere schon ihr Geld verdienten, habe ich sehr hart für einen guten Abschluss gearbeitet und sogar einen kleinen Schuldenberg angehäuft, der bis heute noch nicht vollständig abbezahlt ist. Ich könnte also mit Stolz meinen Titel tragen, doch ist er mir schon fast peinlich. Meine Urkunde liegt in irgend einer Kiste. Zum Spaß hatte ich mal den Titel bei meiner Krankenkasse angegeben. Nachdem mich dann mein Zahnarzt ungläubig angesehen hat und mich mit einem grinsen fragte: <q>Was sind das für Buchstaben vor ihrem Namen?</q>, hatte ich ihn schnell wieder entfernt.</p>
<p><img src="/images/titel1.png" alt="titel"></p>
<p>Der Titel selbst war bei keinem meiner Jobs ein Einstellungskriterium. Vielmehr zählte das, was ich bisher an Projekten gemacht hatte -- fast so wie bei Künstlern oder Fotografen mit ihrer Mappe. Das Github-Profil ist aussagekräftiger als der Abschluss. Eigentlich nicht verwunderlich: Der Markt sucht verzweifelt nach <em>Cloud Backend Specialists</em>, <em>Senior SCRUM Master</em>, <em>Principal Software Architects</em> und <em>Chief Technology Officers</em>. Ins lächerliche gezogen wird es dann mit <em>Code Ninjas</em> und <em>Javascript Hackern</em>. Manche Titel machen nur noch Firmenintern Sinn. Keiner versteht mehr, was sich hinter den (Job) Titeln verbirgt, denn jeder nennt sich wie er möchte. Ob hinter einem <em>Lead Software Architect</em> ein Schreiner mit abgebrochener Lehre, ein Bachelor, Master, Doktor oder Graf von und zu steckt, ist völlig irrelevant. Dabei versucht sich die Branche seit Jahrzehnten mit der des Handwerks zu vergleichen (was allein einen eigenen Artikel füllen würde).</p>
<p>Um diesem Chaos zu entfliehen gibt es verschiedene unabhängige Schulungen wie <a href="https://de.wikipedia.org/wiki/PRINCE2">PRINCE2</a> oder die <a href="https://training.linuxfoundation.org/">Linux Foundation Schulungen</a>, um irgend wie auf einen gemeinsamen Nenner zu kommen. Größere Firmen wie Microsoft (<a href="https://www.microsoft.com/en-us/learning/mcsa-certification.aspx">MCSA</a>), <a href="http://www.cisco.com/c/en/us/products/conferencing/webex-training-center/index.html">Cisco</a>, <a href="http://education.oracle.com">Oracle</a> oder <a href="http://www.delltrainingcentre.com/Main/Home/Home.aspx?ReturnUrl=%2f">Dell</a> haben den Braten gerochen und bieten Partnerprogramme an, um die Firmen noch weiter an die eigenen Produkte zu binden. Win Win.</p>
<p>Warum also einen Titel in der Software/IT-Branche, wenn er sowieso bedeutungslos ist? Warum überhaupt studieren, wenn es beim Bewerbungsgespräch keine Rolle spielt? Diese Frage habe ich mir auch gestellt. Strebt man keine Karriere im Akademischen Umfeld an, ist sie durchaus berechtigt.</p>
<p>Wer studiert und sich einen Titel des Titels willens erarbeitet, wird meiner Meinung in der Software/IT-Branche überrascht sein, dass es hier etwas anders läuft. Natürlich kommt es immer darauf an, wo man danach arbeitet. Große Konzerne wie IBM haben eine Formel zur Berechnung des Einstiegsgehalts. Hier macht sich natürlich ein Titel deutlich bemerkbar. Doch wer nur wegen dem finanziellen <q>Vorsprung</q> studiert, kann sich diesen auch ohne Studium mit der gewonnenen Zeit erarbeiten.</p>
<p>Ich habe in meiner Studienzeit einiges gelernt, was ich vermutlich nicht gelernt hätte wenn ich direkt angefangen hätte zu arbeiten. Eigenverantwortung, Selbstdisziplin, Durchhaltevermögen, wissenschaftliches Arbeiten, den Mut sich mit einem Problem grundlegend zu befassen (nicht nur googlen), Grundlagen verstehen, mit Büchern arbeiten, Informationen abseits des Internets beschaffen, Umgang mit Konflikten und extremen Stresssituationen, Arbeiten im Team, ...</p>
<p>Das war es mir wert. Nicht wegen dem Titel, wegen den Erfahrungen. Und ich glaube damit bin ich nicht allein. Studieren ist also nicht sinnlos, es ist nur die Frage, was man daraus macht.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/251
2016-06-19T23:55:00+02:00
2016-06-19T23:55:00+02:00
Quake 1 Modding: QuakeC programmieren
<p>Kommen wir zum finalen Part der Quake Modding Serie. Wer die anderen beiden Teile noch nicht gesehen hat, sollte das <a href="/tqn">zuvor</a> <a href="/nrq">nachholen</a>. Wir werden ein paar Änderungen am <code>id1</code> Modcode vornehmen und neue Funktionen einbauen, die wir mit dem Map-Editor nicht machen können.</p>
<p>Als erstes müssen wir wieder ein paar Vorbereitungen treffen, um mit dem Sourcecode arbeiten zu können. Im Spieleverzeichnis liegt bereits das <code>id1</code> Verzeichnis. Das ist sozusagen die Haupt-Mod, welche von der Engine beim Starten geladen wird. Weitere Mods können in Form weiterer Verzeichnisse im Spieleverzeichnis abgelegt und im Spiel aktiviert werden (Options -> Browse Mods und dann zum aktivieren Pfeil nach rechts). Alternativ kann man Quake beim Start auch den Parameter <code>-game <modfolder-name></code> mitgeben, um die Mod automatisch zu aktivieren. In unserem Fall legen wir uns hier ein neues Verzeichnis an (Name entspricht dem Modnamen). Hierin legen wir das Verzeichnis <q>qc</q> an, in das wir die Kopie der <a href="https://icculus.org/twilight/darkplaces/files/id1qc.zip">original QuakeC files von id1</a> ablegen.</p>
<p>Der Ladevorgang einer Mod ist analog zum Rest: Werden Dinge mit dem gleichen Namen definiert, werden sie überschrieben. In unserem Fall wird alles überschrieben. Das können wir später noch ändern, in dem wir alles was nicht benötigt wird löschen. Um sich mit dem Sourcecode vertraut zu machen ist es aber sehr praktisch, wenn man den kompletten Code zur Verfügung hat.</p>
<p>Die Quake Engine wird mit einem C-Derivat namens QuakeC programmiert. Dabei handelt es sich um eine interpretierte Sprache, die an C angelehnt ist. Mit ihr können so gut wie alle Elemente der Quake Engine verwendet und manipuliert werden. Um unseren Sourcecode in ein <q>Paket</q> für die Engine zu transformieren, benötigen wir dennoch einen Compiler. Es gibt verschiedene QuakeC Compiler, <a href="https://aur.archlinux.org/packages/frikqcc/">frikqcc</a> ist zu empfehlen.</p>
<p>Machen wir eine kleine Änderung, um zu sehen ob alles korrekt funktioniert. Doch bevor wir anfangen, empfiehlt es sich, ein lokales Git-Repo anzulegen, um im Zweifel Änderungen wieder rückgängig machen zu können.</p>
<pre><code>git init .
git add .
git commit -m "Fresh start"
</code></pre>
<p>Öffnen wir die Datei <code>weapons.qc</code> und suchen die Funktion <code>W_FireShotgun</code>. Hier wird die Funktion <code>FireBullets()</code> aufgerufen. Der erste Parameter entspricht der Anzahl der Projektile. Ändern wir die 6 in eine 50 und sehen was passiert. Führen wir den Compiler im <code>qc</code> Verzeichnis aus, wird der Sourcecode compiliert und eine <code>progs.dat</code> erstellt:</p>
<pre><code>$> frikqcc
--------------- frikqcc v2.7 ----------------
defs.qc
subs.qc
fight.qc
[...]
enforcer.qc
oldone.qc
../progs.dat - 0 error(s), 0 warning(s)
press a key
</code></pre>
<p>Wenn wir Quake starten und die Mod aktivieren, sollte die Shotgun <q>etwas</q> mehr Power haben :)</p>
<h2 id="schritte">Schritte</h2>
<p>Fangen wir mit etwas einfachem an: Wenn der Spieler läuft, wollen wir Schritt-Sounds abspielen, um etwas mehr Ambiente ins Spiel zu bringen. Dazu erstellen wir zuerst vier unterschiedliche Schritt-Sounds als WAV und legen diese im Unterverzeichnis <code>player</code> ab. Ich habe mir hier kurzerhand ein paar eigene Schritte aufgenommen und mit <a href="https://www.archlinux.org/packages/extra/x86_64/audacity/">Audacity</a> etwas schneller gemacht und vier einzelne Schritte isoliert abgespeichert. Als nächstes ist der Code dran. Die WAV-Files müssen wir zuerst laden, dies machen wir am sinnvollsten im <code>worldspawn</code>. Dazu die Datei <code>world.qc</code> öffnen und in der Funktion <code>worldspawn</code> die WAV-Dateien laden:</p>
<pre><code>precache_sound ("player/foot1.wav"); // running
precache_sound ("player/foot2.wav"); // running
precache_sound ("player/foot3.wav"); // running
precache_sound ("player/foot4.wav"); // running
</code></pre>
<p>Sobald der Spieler läuft, wollen wir in zufälliger Reihenfolge die vier Sound Dateien abspielen. Dazu nehmen wir in der Datei <code>player.qc</code> zu Beginn der Funktion <code>player_run</code> folgende Erweiterung vor:</p>
<pre><code>if ((self.walkframe == 1 || self.walkframe == 4)
&& checkbottom(self) == TRUE
&& self.waterlevel == 0) {
local float r;
r = rint(random() * 3);
if (r == 0)
sound (self, CHAN_AUTO, "player/foot1.wav", 0.5, ATTN_NORM);
else if (r == 1)
sound (self, CHAN_AUTO, "player/foot2.wav", 0.5, ATTN_NORM);
else if (r == 2)
sound (self, CHAN_AUTO, "player/foot3.wav", 0.5, ATTN_NORM);
else
sound (self, CHAN_AUTO, "player/foot4.wav", 0.5, ATTN_NORM);
}
</code></pre>
<p>Wenn der Spieler nicht gerade im Wasser ist und nicht springt, wählen wir eine Zufallszahl von 0 bis 3 und spielen dann die WAV-Datei ab. Da Strings in QuakeC unveränderbar (immutable) sind, kommen wir leider um dieses if/else if Konstrukt nicht drumherum. Eine kurz und knackige Übersicht der Funktionen und Konstanten ist <a href="http://www.cataboligne.org/extra/qcmanual.html">hier zu finden</a>. Compiliert alles, können wir es direkt ingame testen.</p>
<h2 id="bluten">Bluten</h2>
<p>Als nächstes wollen wir etwas mit Sprites machen. Hier können wir uns gleich bei einer schon fertigen Funktion bedienen. Wenn ein Spieler getroffen wird, wird die Funktion <code>SpawnBlood</code> aufgerufen. Wir können diese Funktion auch aufrufen, wenn der Spieler nur noch wenig Gesundheit hat -- als würde er bluten. </p>
<p>Öffnen wir die <code>client.qc</code> dazu. Ganz oben müssen wir zuerst die Funktion <code>SpawnBlood</code> bekannt machen, damit wir sie weiter unten verwenden können.</p>
<pre><code>void(vector org, vector vel, float damage) SpawnBlood;
</code></pre>
<p>In der Funktion <code>PlayerPreThink</code> können wir dann -- abhängig von <code>self.health</code> -- Blut erzeugen:</p>
<pre><code>if (self.health <= 65) {
local float freq;
freq = rint(self.health / 20);
if (rint(random() * freq) == 1
&& rint(random() * 3) == 1)
SpawnBlood (self.origin, '0 0 0', 70);
}
</code></pre>
<p>Eigentlich recht simpel: Wenn <code>self.health</code> unter 65 sinkt, erstellen wir eine Variable <code>freq</code>, die abhängig von der Gesundheit ist. Bei voller Gesundheit ist sie 5, bei 10 Lebenspunkten nur noch 1. Danach nehmen wir den Zufallsgenerator, um die Wahrscheinlichkeit zu ermitteln, ob ein Blut-Sprite erstellt werden soll oder nicht. Je niedriger die Lebensenergie ist, desto mehr Blut.</p>
<p><img src="/images/quake1_bleeding.png" alt="bleeding"></p>
<p>Fehlt nur noch eine <a href="http://www.insideqc.com/qctut/qctut-8.shtml">Flashlight</a> und ein paar düstere Maps und fertig ist die Horror-Mod.</p>
<p>Wie man sieht, ist es recht einfach, die Engine zu modden. Nachdem man sich etwas mit dem Sourcecode vertraut gemacht hat, findet man schnell die passende Stelle. Zusammen mit dem <a href="http://www.cataboligne.org/extra/qcmanual.html">Manual</a> kommt man hier recht schnell zum Ziel. Auf <a href="http://www.insideqc.com/qctut/">InsideQC</a> gibt es zudem sehr viele Tutorials zu allen möglichen Themen.</p>
<h2 id="ausblick">Ausblick</h2>
<p>Das war er also, der kleine Rundumschlag zur Quake 1 Engine. In <a href="/tqn">Teil 1</a> haben wir uns das Map-Format genauer angesehen und uns etwas mit dem Netzwerkprotokoll beschäftigt. In <a href="/nrq">Teil 2</a> haben wir uns die verschiedenen Pack-Formate (PAK und WAD) angesehen, Texturen erstellt, eine Map gebaut und compiliert. Hier haben wir uns auch die Möglichkeiten angesehen, die wir direkt im Map-Editor haben, um die Engine zu beeinflussen. Im letzten Teil haben wir uns den QuakeC Sourcecode des Originalspiels (id1) näher angesehen und ein paar Erweiterungen eingebaut.</p>
<p>Mit diesem Handwerkszeug steht der eigenen Mod nichts mehr im Wege. (Ein wichtiges Themengebiet haben wir allerdings noch ausgelassen: 3D-Models. Da dies ein recht umfangreiches Thema ist, habe ich es bewusst hier nicht behandelt. Wer sich damit näher beschäftigen möchte, kann sich den recht aktuellen <a href="http://pnahratow.github.io/creating-models-for-quake-1.html">Artikel von Philipp Nahratow</a> ansehen.)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/250
2016-05-23T22:46:00+02:00
2016-05-23T22:46:00+02:00
Quake 1 Modding: Mapping
<p>Im letzten Teil haben wir uns die Quake 1 Engine genauer angesehen. In diesem Teil wollen wir sie modifizieren. Wir schauen uns an, wie die Quake Engine Änderungen erlaubt und wie Maps aufgebaut und compiliert werden.</p>
<h2 id="assets">Assets</h2>
<p>Alle für das Spiel notwendigen Dateien werden in sog. <strong>PAK</strong>-Dateien gespeichert. Diese enthalten ähnlich wie ein Zip-Archiv ein Verzeichnisbaum, in dem Dateien abgelegt sind. Die <code>*.pak</code>-Dateien des Originalspiels sind im <code>id1</code>-Verzeichnis des Spiels zu finden. Liegen hier mehrere <code>*.pak</code>-Dateien, werden sie in alphabetischer Reihenfolge geladen. Für Mods sind PAK-Dateien ideal, da sie einfach zu handhaben sind und alles enthalten was benötigt wird.</p>
<p>Die Quake-Engine lädt nun eine PAK-Datei nach der anderen, und <q>überlagert</q> gleichnamige Dateien. Sind also zwei Dateien <code>pak0.pak</code> und <code>pak1.pak</code> vorhanden, in denen jeweils eine Map <code>intro.bsp</code> liegt, wird zuerst die Map aus <code>pak0.pak</code> geladen und dann mit der aus <code>pak1.pak</code> ersetzt. Zuletzt wird noch geprüft ob im <code>id1</code>-Verzeichnis ebenfalls eine <code>intro.bsp</code> liegt, welche dann ebenfalls die davor geladene Map überlagert. Man spricht hier auch von einem <q>Overlay-Dateisystem</q>.</p>
<p>So lässt sich jeder Teil (bis auf die Kernfunktionen Netzwerk, Rendering, Usereingaben etc.) überschreiben und modifizieren. Wird das ganze Spiel auf diese Weise umgebaut, nennt man es auch <q>Total Conversion</q>.</p>
<h3 id="texturen-wad">Texturen (WAD)</h3>
<p>Texturen können in <strong>WAD</strong>-Dateien gruppiert werden, was sehr gerne bei <q>Textur-Packs</q> gemacht wird. WAD-Dateien sind -- ähnlich wie PAK-Dateien -- Container für eine Ansammlung von Dateien. In dem Fall ohne Verzeichnisstruktur. Die Vorgaben für Texturen sind hier relativ restriktiv. Dies macht es aber der Engine einfacher mit den Dateien zu arbeiten.</p>
<p>Texturen müssen im in Länge und Breite ein Vielfaches von 16px haben und in der 8 Bit Farbpalette gespeichert werden. Waser/Lava-Texturen müssen 64x64px große sein, Himmel-Texturen sind 256x128px. Größer als 320x192 dürfen sie aber nicht sein. Die Dateibenennung ist ebenfalls entscheidend. Flüssigkeiten fangen mit einem <code>*</code> an. Das Präfix <code>slime</code> und <code>lava</code> macht die Flüssigkeit automatisch zu Schleim oder Lava. Alles andere ist Wasser (kein Schaden).</p>
<p>Animationen fangen mit einem <code>+</code> an, gefolgt von der Framenummer. Eine Animationstextur mit zwei Frames besteht also aus zwei Dateien <code>+0button</code> und <code>+1button</code>. Himmel-Texturen fangen mit <code>sky</code> an.</p>
<p>Machen wir uns eine neue Textur und stecken diese in eine WAD-Datei, die wir dann vom Map-Editor und von der Quake Engine laden können. Mit Gimp lässt sich das <a href="https://www.gimp.org/tutorials/Tileable_Textures/">einfach erledigen</a>. Wichtig dabei ist, dass die Textur kachelbar ist, also dass der rechte Rand zum linken Rand passt und nahtlos übergeht.</p>
<p><img src="/images/textur_tile.png" alt="textur"></p>
<p>Dabei teilt man die Textur in 4 Bereiche und dreht diese so, dass die inneren Seiten nach außen zeigen. Das schwierigste dabei ist es, die äußeren Seiten im Inneren zusammenzuführen. Abschließend noch die Farben auf 256 reduzieren. Für den Dateinamen verwende ich gerne ein Prefix, so dass sie nicht mit anderen Texturen kollidiert.</p>
<p>Sind wir mit der Textur zufrieden, können wir sie in eine WAD-Datei stecken. Ich verwende dafür das Tool <a href="https://aur.archlinux.org/packages/qpakman/">qpakman</a>.</p>
<pre><code>qpakman -g quake1 fu_stonefloor.png -o fu.wad
</code></pre>
<p>Zum Test, ob alles geklappt hat, können wir uns den Inhalt der WAD-Datei ansehen.</p>
<pre><code>$ qpakman -l fu.wad
**** QPAKMAN v0.67 (C) 2008 Andrew Apted ****
Opened WAD2 file: fu.wad
--------------------------------------------------
1: +0000000c 0000bf68 M : fu_stonefloor
--------------------------------------------------
Closed WAD2 file
</code></pre>
<h2 id="mapping">Mapping</h2>
<p>Bevor wir mit der Map anfangen können, müssen wir noch ein paar Dinge einrichten. Ich arbeite mit <a href="http://ingar.satgnu.net/gtkradiant/">NetRadiant</a> (ein Ableger von GtkRadiant), da diese Version aktueller ist und mehr Features bietet. GtkRadiant geht natürlich auch. Nach der Installation benötigen wir zwei Dinge:</p>
<ul>
<li>Eine Quake 1 Installation (siehe <a href="/tqn">Teil 1</a>)</li>
<li>Das Quake 1 <a href="http://www.bspquakeeditor.com/files/quake_wad.zip">WAD-File</a> aus der PAK-Datei des id1 Ordners</li>
</ul>
<p>Beim Start von NetRadiant wird zuerst das Spiel abgefragt. Hier bitte Quake wählen, auch wenn wir Darkplaces (als Engine) verwenden. So findet NetRadiant dann später die Texturen.</p>
<p><img src="/images/netradiant01.png" alt="NetRadiant"></p>
<p>Danach noch schnell in die Settings (Edit->Settings) und den Pfad zur Quake 1 Installation angeben (den <code>id1</code>-Ordner auswählen). In diesem Ordner sollten dann auch die WAD-Dateien liegen. Danach sollte der Editor folgendermaßen aussehen:</p>
<p><img src="/images/netradiant02.png" alt="NetRadiant"></p>
<p>Nun können wir loslegen. Aber eines vorweg: Dies hier ist nicht als vollwertiges Handbuch zu NetRadiant zu verstehen. Ich zeige nur die minimalen Funktionen auf, die nötig sind, um eine ganz einfache Map zu bauen. Wer daran Gefallen findet, kann gerne <a href="https://www.google.de/search?q=%28gtkradiant+%7C+netradiant+%7C+qeradiant%29+mapping+tutorial">hier</a> weitermachen. Auch ganz hilfreich sind die <a href="http://ingar.satgnu.net/gtkradiant/shortcuts.html">Shortcuts</a>.</p>
<p>Eine Map besteht aus Brushes (mit Texturen) und Entities. Brushes sind die <q>Bauklötze</q>, aus der die Map besteht. Diese können von allen Seiten mit jeweils einer Textur überzogen werden. Entities sind Elemente wie Waypoints, Waffen oder Spawn-Points, die in der Map platziert werden können. Wir werden für unsere erste Map 6 Brushes, zwei <code>light</code> Entities und ein <code>info_player_start</code> Entity brauchen.</p>
<p>Fangen wir mit dem <code>info_player_start</code> Entity an. Es hilft uns, die Größenverhältnisse etwas besser abzuschätzen. Rechtsklick auf die Arbeitsfläche und dann unter <q>info</q> -> <q>info_player_start</q> auswählen. Ein roter Kasten mit einem Pfeil taucht auf. Der Spieler ist in etwas so groß und wird an genau dieser Position starten, wenn die Map geladen wird. Der Pfeil zeigt die Blickrichtung an. Mit der Maus kann das Entity verschoben werden. Mit ESC wird die Auswahl abgewählt.</p>
<p>Als nächstes bauen wir den Raum. Dazu genügt es, mit der Maus ein Rechteck aufzuziehen. Mit CTRL+TAB kann die Ansicht gewechselt werden. So positionieren/skalieren wir den Block so lange, bis er als Bodenfläche passt. Mit ESC abwählen und wieder mit der Maus ein Rechteck aufziehen. So verfahren wir mit allen 4 Seitenwände, dem Boden und der Decke. Wichtig dabei ist allerdings, dass die so gebaute Box komplett dicht ist. Mit der <q>Snap to Grid</q> Funktion klappt das recht gut.</p>
<p><img src="/images/netradiant03.png" alt="netradiant einrichten"></p>
<p>Ist das geschafft, können wir noch die beiden Lichter in unserem Raum platzieren, so dass wir die Map später auch bewundern können. Dazu wieder mit einem Rechtsklick auf die Arbeitsfläche und <q>light</q> auswählen. Als Wert (Helligkeit) geben wir hier 160 an, das sollte genügen.</p>
<p><img src="/images/netradiant04.png" alt="netradiant einrichten"></p>
<p>Was jetzt noch fehlt, sind Texturen. Mit gehaltener SHIFT-Taste können wir mit der Maus in der 3D-Ansicht die Wand selektieren. Ist sie rot markiert, kann in dem darunterliegenden Texturbrowser eine Textur ausgewählt werden. Die Taste S öffnet den Surface Inspector, in dem die Textur dann noch bearbeitet werden kann (Position, Skalierung, Rotation, ...).</p>
<p><img src="/images/netradiant07.png" alt="netradiant einrichten"></p>
<p>Soweit sogut. Eine Kleinigkeit müssen wir noch einstellen, bevor wir die Map compilieren und testen können. Wir haben Texturen aus WAD-Dateien verwendet, die wir noch angeben müssen. Dazu L für <q>Entity list</q> drücken und dort <q>worldspawn</q> auswählen. Danach N für <q>Entities</q> drücken. Hier dann bei <q>Wad File</q> die beiden Dateien -- getrennt durch Semikolon -- angeben und mit ENTER bestätigen.</p>
<p><img src="/images/netradiant06.png" alt="netradiant einrichten"></p>
<p>Speichern nicht vergessen (ich habe die Map unter <code>/usr/share/games/quake/id1/maps/aroom.map</code> gespeichert).</p>
<h2 id="compilierung">Compilierung</h2>
<p>Um die Map nun spielen zu können, müssen wir sie zuvor noch in das BSP-Format bringen. Dazu verwende ich <a href="https://aur.archlinux.org/packages/hmap2/">hmap2</a> vom darkplaces Projekt. Hiermit müssen wir die Map in drei Schritten Compilieren.</p>
<p>Im ersten Schritt ist das BSP Rendering dran. Dabei werden alle nicht sichtbaren Seiten (faces) entfernt und die verbleibenden in einem BSP Baum gespeichert. Spätestens hier können wir sicherstellen, dass unsere Map von allen Seiten her <q>dicht</q> ist. Ist dies nicht der Fall, wirft der Compiler Fehlermeldungen.</p>
<pre><code>cd /usr/share/games/quake/id1/maps/
hmap2 aroom.map
</code></pre>
<p>Im nächsten Schritt teilen wir die Map in <q>Portale</q> ein. Dies ist eine Optimierung für die Engine, welche wir auch überspringen können. Bei kleinen Maps macht dies bei einem aktuellen PC keinen spürbaren Unterschied. Doch bei größeren Maps macht dieser Schritt immer Sinn.</p>
<pre><code>hmap2 -vis aroom.map
</code></pre>
<p>Und im letzten Schritt berechnen wir das Licht. Hat die Map ein Leak, kann dieser Schritt nicht ausgeführt werden und die Map wird überall gleich beleuchtet.</p>
<pre><code>hmap2 -light aroom.map
</code></pre>
<p>Danach haben wir nun eine <code>aroom.bsp</code>, die wir mit der Quake Engine starten können.</p>
<h2 id="testen">Testen</h2>
<p>Wir starten die Quake Engine mit <code>darkplaces-sdl</code>, öffnen mit <code>~</code> die Console und geben <code>map aroom</code> ein. Die Map sollte nun laden und uns an der von uns festgelegten Position starten lassen.</p>
<p><img src="/images/netradiant05.png" alt="netradiant einrichten"></p>
<p>Unsere erste Map ist fertig. </p>
<h2 id="map-ausbauen">Map ausbauen</h2>
<p>Das waren natürlich nur die minimal notwendigen Schritte, um eine Quake 1 Map zu erstellen und zu starten. Wir können nun die Map noch etwas ausbauen und mit Aktionen ausstatten. Versuchen wir folgendes: Sobald alle Gegner getötet wurden, öffnet sich eine Türe.</p>
<p>Zuerst erstellen wir die Türe. Dazu ziehen wir einen Brush auf, der einer Türe entspricht. Im noch ausgewählten Zustand dann mit dem Kontextmenü in eine <code>func_door</code> wandeln. (Rechtsklick <q>func</q> -> <q>func_door</q>). Die Einstellungen zur Türe können wir wie gewohnt mit <strong>N</strong> öffnen. Hier einen Haken bei <q>toggle</q> setzen und <q>Open Direction</q> auf <q>up</q> stellen (angle = -1). Als nächstes platzieren wir unsere drei Monster im Raum. Ich habe Knights genommen, um es ein bisschen anspruchsvoller zu machen :)</p>
<p><img src="/images/netradiant08.png" alt="netradiant einrichten"></p>
<p>Als letzten Schritt müssen wir noch unsere Logik einbauen. Dazu wieder einen kleinen Brush aufziehen und mit dem Kontextmenü zu einem <code>trigger_counter</code> wandeln. In den Einstellungen (N drücken) setzen wir <strong>Count</strong> auf 3. Jetzt wird es spannend: Wir selektieren alles ab (ESC), danach selektieren wir den ersten <code>monster_knight</code> und dann zusätzlich den <code>trigger_counter</code>. Nach einen Druck auf <q>CTRL+k</q> verbinden wir beide und eine rote Linie ist zu sehen. Dies machen wir mit den zwei verbleibenden <code>monster_knight</code>s auch. Und anschließend verknüpfen wir so den <code>trigger_counter</code> mit der <code>func_door</code>. Eine lila Linie sollte erscheinen. Werden jetzt alle drei Monster getötet -- also der <code>trigger_counter</code> drei mal aktiviert -- wird dieser wiederum die Türe aktivieren.</p>
<p>Ich habe noch eine Waffe und etwas Munition, sowie ein Medpack hinzugefügt, um dem Spieler eine reelle Chance zu lassen :)</p>
<p><img src="/images/netradiant09.png" alt="netradiant einrichten"></p>
<p>Durch geschickte Kombination von Entities lassen sich so ganze Missionen erstellen. Wer mehr will, muss QuakeC schreiben. Und genau das werden wir im nächsten Teil machen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/254
2016-05-19T21:30:00+02:00
2016-05-19T21:30:00+02:00
Ich, die Werbeplattform
<p>Ich nehme Euch als Leser meiner Seite ernst. Ich halte euch für kompetent und intelligent. Deshalb gebe ich mir beim Schreiben der Artikel viel Mühe. Auf meiner Seite werdet ihr keine Werbung finden, auch nicht versteckt. Niemand muss hier Angst haben, auf einen <q>falschen</q> Download-Link zu klicken. Und so wird es auch bleiben.</p>
<p>Anfragen wie diese hier haben da leider keine Chance.</p>
<blockquote>
<p>Guten Tag,</p>
<p>mein Name ist XXX und ich und meine Firma sind momentan auf der Suche nach Seiten, die gegen Aufwandsentschädigung Gastartikel veröffentlichen. Unser aktueller Kunde ist ein renommierter Anbieter aus der iGaming Branche und aaron-mueller.de passt gut in unser Konzept.</p>
<p>Wenn Sie sich dazu entscheiden, einen Beitrag von uns anzunehmen, verfasst einer unserer Autoren einen Artikel, der individuell auf Ihre Seite angepasst ist, also zu den bereits vorhandenen Themen passt und Ihren Lesern einen Mehrwert bietet. An der Veröffentlichung von reinen Werbeartikeln haben wir genauso wenig Interesse wie Sie.
Lassen Sie mich wissen, ob sie interessiert sind!</p>
</blockquote>
<p>Ich schreibe hier aus Überzeugung und weil es mir Spaß macht. Nicht wegen dem Geld und auch nicht wegen den <a href="/stats">Klicks</a>. <a href="https://www.youtube.com/watch?v=HQ7S2fMSzy8">Fynn Kliemann</a> hat das <a href="https://twitter.com/fimbim/status/729395634051133440">ganz passend</a> gesagt:</p>
<blockquote>
<p><q>Ich mach das hier aus Bock</q></p>
</blockquote>
<p>Das wollte ich nur mal gesagt haben.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/253
2016-05-18T23:40:00+02:00
2016-05-18T23:40:00+02:00
Elektronik: Pumpencheck
<p>In meinem Keller gibt es einen <a href="https://de.wikipedia.org/wiki/Pumpensumpf">Pumpensumpf</a>, in dem sich Regenwasser und Grundwasser sammelt und durch eine Pumpe mit <a href="https://de.wikipedia.org/wiki/Schwimmerschalter">Schwimmschalter</a> abgepumpt wird. An regnerischen Tagen steigt dort der Wasserpegel, der Schwimmschalter löst aus und das überschüssige Wasser wird abgepumpt. In der Theorie optimal, doch was wenn die Pumpe umkippt, kaputt geht oder verstopft? Dann staut sich das Wasser an und dringt in den Keller ein. Um das zu vermeiden habe ich mir ein Monitoring-System gebaut, welches die Pumpe im Fehlerfall auch abschalten kann.</p>
<p>Die Idee ist recht simpel: Zwischen Steckdose und Pumpe wird der Stromverbrauch kontinuierlich gemessen. Steigt dieser an, ist die Pumpe aktiv, sinkt er, ist die Pumpe aus. Somit muss die Pumpe selbst nicht modifiziert werden und kann in ihrem wasserdichten Gehäuse bleiben. Über ein Relais kann dann die Stromzufuhr zur Pumpe unterbrochen werden.</p>
<p><img src="/images/pumpe01.jpg" alt="pumpencheck"></p>
<p>Zur Steuerung habe ich das <a href="http://www.wemos.cc/Products/d1_mini.html">Wemos D1 Mini Development Board</a> verwendet, damit ich Messpunkte direkt in meine <a href="https://influxdata.com/">InfluxDB</a> pumpen kann, um es so in <a href="http://grafana.org/">Grafana</a> zu visualisieren.</p>
<p>Der Sensor ist ein ACS712, den es für ein paar EUR bei eBay gibt. Ich habe die 20A Variante gewählt, was sich als etwas unklug erwiesen hat. Meine Pumpe verbraucht 220W, also ca. 1A. Am Ausgang vom Sensor liegen 2.5V ohne Last an und jeweils 0.1V pro 1A Last. Ist die Pumpe in Betrieb, liegen also ca. 2.6V an. Bei der 5A-Variante des Sensors wäre die Auflösung viel größer. Zudem ist der Sensor nicht sehr akkurat und der USB-Charger, sowie die Grundlast der Pumpe beeinflusst den Sensor ebenfalls. Dies musste ich Codeseitig kompensieren.</p>
<p>Ursprünglich wollte ich für die Stromversorgung einen kleinen LiPo-Akku verwenden, doch hatte ich noch einen alten, kleinen USB-Charger übrig, der perfekt in das Gehäuse passte. Somit ist das Gerät auch für den Dauerbetrieb geeignet und erfordert keinerlei manuellen Eingriff mehr.</p>
<p>Problematisch war noch das Relais, das ich verwenden wollte. Es schaltete erst ab 5V, der ESP8266 liefert aber 3.3V als HIGH Pegel. Mit etwas Hilfe von meinem <a href="http://klaute.de/">Elektronik-Mentor</a> war das Problem aber schnell behoben :)</p>
<p>Alles verklebt, verlötet und verschraubt sieht das Innenleben nun wiefolgt aus:
<img src="/images/pumpe03.jpg" alt="pumpencheck"></p>
<p>Der <a href="http://git.datenhalter.de/aaron/pumpcheck/src/master/pumpcheck.ino">Code</a> ist recht übersichtlich. Beim Start werden 30 Messwerte im Abstand von 0.5 Sekunden vom Sensor entnommen und davon dann den Mittelwert gebildet. Dieser Wert ist fortan der Richtwert für <q>Pumpe aus</q>. Weicht der Messwert 10 Einheiten von diesem Wert ab, läuft die Pumpe.</p>
<p>Länger als 10 Minuten sollte die Pumpe nicht laufen. Läuft sie länger, liegt irgend ein Problem vor (Pumpe umgefallen/verstopft, Schwimmschalter kaputt, ...). Ist das der Fall, schaltet das Relais die Stromzufuhr für 30 Minuten ab und versucht es dann erneut.</p>
<p>Alle Aktionen (Pumpenaktivität, Notabschaltungen) werden als Messwerte in einer InfluxDB gespeichert. Als zusätzlicher Aktivitätsindikator habe ich noch eine LED eingebaut.</p>
<p><img src="/images/pumpe02.jpg" alt="pumpencheck"></p>
<p>Feineinstellungen am Code kann ich nun übrigens bequem vom PC aus per <a href="http://esp8266.github.io/Arduino/versions/2.0.0/doc/ota_updates/ota_updates.html">Over The Air Update</a> durchführen und muss nicht mit einem Kabel in den Keller :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/248
2016-05-03T20:00:00+02:00
2016-05-03T20:00:00+02:00
Quake 1 Modding: Die Engine
<p>Diesen Artikel wollte ich schon sehr, sehr lange schreiben :) Vor ca. 20 Jahren kam das Spiel Unreal auf den Markt. In Deutschland war es noch nicht erhältlich, doch ich hatte das Glück mit der Schule eine Klassenfahrt nach London machen zu dürfen :) Die komplette Busfahrt nach Hause habe ich die Schachtel beäugt und die Bedienungsanleitung gelesen. Zuhause angekommen stellte ich aber fest, dass mit meiner derzeitigen Grafikkarte nicht viel mit dem Spiel anzufangen war. Also kaufte ich zusammen mit meinem Bruder eine 3dfx Voodoo Grafikkarte mit 8MB Speicher. Das Teil war ein Monster. Die Begeisterung für FPS-Games war bei uns geboren. Schnell fanden wir heraus wie man eigene Maps baut und hatten viel Spaß damit. Ein Jahr später brachte mein kleiner <a href="http://rubenartus.com/">Bruder</a> das Spiel HalfLife von einem Freund mit nach Hause. Eine neue Liebe begann. Der Singleplayer-Modus war super, doch die ganzen Mods waren noch viel besser. <a href="https://wiki.teamfortress.com/wiki/Team_Fortress_Classic">Team Fortress Classic</a>, <a href="http://www.moddb.com/mods/front-line-force">Frontline Force</a>, <a href="http://scienceandindustrygame.com/">Science and Industry</a>, <a href="http://www.svencoop.com/">Sven Co-Op</a> (was für ein Spaß!), natürlich <a href="http://unknownworlds.com/ns/about/">Natural Selection</a> und <a href="https://en.wikipedia.org/wiki/Counter-Strike">Counter Strike</a>, und und und ... und auch die Singleplayer-Episoden wie <a href="http://www.moddb.com/mods/they-hunger">They Hunger</a> oder das geniale <a href="http://www.poke646.com/">Poke646</a> waren der Wahnsinn.</p>
<p>Ich wollte wissen, wie das geht. Wie man Maps baut, wie man Texturen erstellt, wie man Missionen scripted, wie man sich eine Armbrust mit aufgesetzter Bullet-Cam programmiert, wie man neue Gegner erstellt, wie man 3D-Modelle konstruiert, ... Ich habe Jahre meiner Kindheit/Jugend mit der HalfLife-Engine verbracht. Ich lebte quasi im <a href="http://www.thewall.de/forum/">TheWall-Forum</a>. Ich lernte dort Freunde kennen und wir hatten alle einen großen Traum: Ein eigenes Spiel machen. Wir gingen regelmäßig auf die <a href="http://dusmania5.spieleentwickler.org/galerie/index.htm">Dusmania</a> und lernten andere begeisterte Spielemacher kennen. Ich wurde Teil der <a href="http://zfx.info/frontpage.php">Developia</a> Community und gründete sogar meine eigene <a href="https://web.archive.org/web/20030604061802/http://tutorial-world.net/?tab=tutorials§ion=2">Tutorial-Seite</a>. Dies brachte mich erst richtig zum Programmieren und wurde dann schlussendlich zu meinem Beruf. Natürlich spielte ich auch andere Spiele und <a href="https://en.wikipedia.org/wiki/Unreal_Tournament">Unreal Tournament</a> war lange Zeit die Nummer 1, doch hat mich die Quake-Engine von Anfang an begeistert.</p>
<p>Ich möchte euch auf eine kleine (mehrteilige) Reise in die Welt des Moddings nehmen. Wir werden uns die Quake 1 Engine einmal von innen ansehen und im Anschluss selbst eine kleine Mod erstellen. Quake ist dieses Jahr 20 Jahre alt geworden, was aber kein Grund ist, sich nicht mehr damit zu beschäftigen. Viele der Techniken sind in sehr ähnlicher Form auch in den heutigen Spielen vertreten. Einige Tools werden heute noch verwendet. Also nehmt euch etwas Zeit und macht mit. (Ich verwende ausschließlich Linux-Tools, vermutlich gibt es aber für jedes hier vorgestellte Tool ein Windows-Äquivalent)</p>
<p>Fangen wir ganz am Anfang an: 1996 programmierte <a href="https://en.wikipedia.org/wiki/John_Carmack">John Carmack</a> zusammen mit seinen Co-Foundern von id Software Quake. Die Engine war so gut durchdacht, dass schnell <a href="https://upload.wikimedia.org/wikipedia/commons/6/63/Quake_-_family_tree.svg">viele weitere Spiele</a> damit entwickelt wurden -- nicht nur von id Software. Allen voran natürlich Half Life von Valve, auf deren Engine natürlich wiederum eine Unmenge von weiteren Spielen entstand. Einige Jahre später wurde die <a href="https://github.com/id-Software/Quake">Quake 1 Engine unter die Open Source Lizenz</a> gestellt und bald darauf entstanden daraus wieder viele Ableger der Engine.</p>
<h2 id="das-spiel-besorgen">Das Spiel besorgen</h2>
<p>Die Original Quake 1 Engine ist auf einem heutigen PC kaum mehr lauffähig. Doch viele engagierte Programmierer haben sie auf neue Technologien und Betriebssysteme <a href="http://www.quakewiki.net/quake-1-engines/">portiert oder gleich komplett neu geschrieben</a>. Ich verwende die <a href="https://icculus.org/twilight/darkplaces/">Darkplace</a> Modifikation, da Sie einige Features fürs Modding bietet und weiterhin kompatibel zum original Quake 1 ist. Das Paket ist schnell <a href="https://aur.archlinux.org/packages/darkplaces">installiert</a>, dennoch ist die Engine selbst noch etwas langweilig. Die Asset-Files sind leider nicht Open Source und müssen <a href="https://quakewiki.org/wiki/Getting_Started#Getting_Quake">anderweitig</a> <a href="https://www.quaddicted.com/quake/buy">besorgt</a> <a href="https://www.google.de/search?q=quake+1+id1+folder">werden</a>. In Deutschland ist es zudem etwas schwerer an die Assets zu kommen, da Quake 1 zwar seit 2011 nicht mehr indiziert ist, aber eine USK-Prüfung noch immer aussteht. Vermutlich findet sich aber ein Original auf jedem Flohmarkt dieser Welt und ist für ein paar EUR zu haben. Die Assets sind nicht zwingend notwendig, doch extrem praktisch wenn es später ans Modding geht. Hier können wir dann vorhandene Sounds/Texturen wiederverwenden.</p>
<p><img src="/images/quake1.png" alt="quake 1"></p>
<h2 id="die-engine">Die Engine</h2>
<p>1996 waren die meisten PCs nicht in der Lage, aufwändige 3D-Szenen in Echtzeit zu berechnen. Es mussten also ein paar Tricks angewendet werden, um ein solches Spiel überhaupt möglich zu machen. Die Berechnung der Vertexes und deren Kollision mit anderen Entities musste auf ein Minimum reduziert werden, damit das Spiel bei den Spielern ruckelfrei lief -- auch mit einem nicht so gut ausgestatteten PC.</p>
<p>Der erste Trick ist die Reduktion der Brushes (Einzelne <q>Quader</q> in einer Map) auf sichtbare Faces (Flächen). Es werden also alle Seiten des Quaders entfernt, die sowieso nicht sichtbar wären. Diese Optimierung bringt aber den Nachteil, dass die Map <q>dicht</q> sein muss, sonst sähe man unter Umständen die Rückseite, die gar nicht da ist. Diesen Effekt sieht man manchmal wenn man im Spiel an einer Wand steht und den Kamerawinkel entsprechend wählt. Plötzlich hat es den Anschein man könnte durch die Wand schauen.</p>
<p><img src="/images/quake_optimizing.png" alt="quake optimizing"></p>
<p>Zuerst wird ermittelt, was <q>außerhalb</q> und was <q>innerhalb</q> der Map liegt. (Der Himmel ist übrigens nur eine <a href="https://www.google.de/search?q=skybox">spezielle animierte Textur</a>.) Mit dieser Information lassen sich alle Faces berechnen, die nicht sichtbar sind. Diese werden dann gelöscht. Übrig bleiben die Faces, die innen liegen. Die einzelnen Faces werden dann in einem speziellen Binärbaum gespeichert, dazu aber später mehr. Ist die Map nicht 100% nach außen abgeschlossen (ein <q>leak</q>), kann diese Optimierung nicht erfolgen und der Compiler bricht ab.</p>
<p>Die zweite wichtige Optimierung ist die Aufteilung der Map in sichtbare Bereiche. So muss nur ein Bruchteil der Map zur gleichen Zeit verarbeitet und gerendert werden. Hier redet man von <strong>vis</strong>. Dieser Schritt ist optional, beschleunigt aber das Rendering um ein vielfaches. Auch macht es sich später extrem bei der Kollisionserkennung bemerkbar, da nicht mehr alles geprüft werden muss. </p>
<p>Die dritte Optimierung ist die Vorberechnung von Licht. Alle (statischen) Lichtquellen in der Map werden schon zur Compilezeit berechnet. Das spart unglaublich viel Rechenzeit zur Laufzeit. Führt man diese Optimierung nicht durch, ist die Map überall gleich ausgeleuchtet. Der Optimierungsschritt wird oft <strong>light</strong> oder <strong>rad</strong> genannt.</p>
<p>Durch diese Optimierungen ist es möglich, die Map in Echtzeit zu Rendern und nebenbei noch KI und Netzwerkkommunikation zu machen. Dies war für die damalige Zeit unglaublich und brachte zusammen mit Doom das FPS-Genere zutage. Einen kleinen Blick in den Sourcecode der Quake 1 Engine gibt <a href="http://fabiensanglard.net/quakeSource/">Fabian Sanglard in seinem Artikel</a>. Auch sehr spannend ist die <a href="https://www.bluesnews.com/abrash/">Artikelserie von Michael Abrash</a> zum Entstehungprozess der Engine.</p>
<h3 id="das-mapformat">Das Mapformat</h3>
<p>Eine wirklich sehr spannende Sache ist das Format, in der die Map vorberechnet/gespeichert wird. Es nennt sich <a href="http://www.gamers.org/dEngine/quake/spec/quake-spec34/qkspec_4.htm">BSP29</a> und ist eine erweiterte Version eines <a href="https://en.wikipedia.org/wiki/Binary_space_partitioning">BSP-Trees</a> (Binary Space Partitioning, oder auch binäre Raumpartitionierung). Vermutlich hat jeder der irgend was mit Informatik studiert hat schon einmal davon gehört. Es ist ein Binärbaum, der einzelne Abschnitte (<q>Hyperplanes</q>) eines Raumes trennt und zudem festlegt was <q>davor</q> und <q>dahinter</q> liegt.</p>
<p><img src="/images/bsp.gif" alt="bsp tree">
(Copyright © 1995-97, Bretton Wade. <a href="ftp://ftp.sgi.com/other/bspfaq/faq/bspfaq.html#6.txt">source</a>)</p>
<p>Jeder Knoten enthält ein Teilbereich der Map, dessen Kinder die Teilbereiche davor und dahinter entsprechen. Diese Teilbereiche (<q>subspaces</q>) können wieder beliebig oft rekursiv geteilt werden. Als einfaches Beispiel wird ein Raum A in die Teilbereiche B und C an der Linie X geteilt. Der Bereich B wird dann weiter in D und E an der Linie Y geteilt. Der Vorgang wird abgebrochen, wenn der erforderliche Teilungsgrad erreicht ist. Als Ergebnis erhält man dann die Hyperplanes C, D und E, schön geordnet in einem Binärbaum. Auf Wikipedia ist <a href="https://en.wikipedia.org/wiki/Binary_space_partitioning#Generation">der Vorgang gut erklärt</a>.</p>
<p>Mit diesen Informationen kann die Engine dann genau das rendern, das sichtbar ist, und muss keinen Pixel doppelt berechnen. Das folgende Youtube Video demonstriert das nochmal gut:</p>
<iframe width="790" height="500" src="https://www.youtube.com/embed/yTRzfKh4Tg0" frameborder="0" allowfullscreen></iframe>
<p>Den BSP-Tree kann man sich auch im Spiel live ansehen. Dazu auf der Console <code>sv_cheats 1</code>, gefolgt von <code>r_showsurfaces 1</code> eingeben (zumindest bei der darkplaces Engine). Jede farbige Fläche ist eine Hyperplane, die sich im BSP-Baum befindet.</p>
<p><img src="/images/bsp_ingame.png" alt="BSP Tree"></p>
<p>Mit <code>r_drawportals 1</code> lassen sich die <q>Blätter</q> des BSP-Baums ansehen -- auch Portale genannt.</p>
<p><img src="/images/bsp_portals.png" alt="BSP Tree"></p>
<h3 id="netzwerk">Netzwerk</h3>
<p>Das Netzwerkprotokoll ist im Grunde <a href="http://www.gamers.org/dEngine/quake/spec/quake-spec34/qkspec_8.htm">recht simpel</a>, hat aber einige sehr intelligente Ansätze, die mit Quake das FPS Genere erst möglich gemacht hat. Details zur Implementierung ist ebenfalls bei <a href="http://fabiensanglard.net/quakeSource/quakeSourceNetWork.php">Fabien Sanglard zu finden</a>.</p>
<p>Entscheidend bei FPS Spielen ist die Latenz. Sprich, wie lange braucht mein Tastendruck bis er bei den Mitspielern ankommt? Der Server (ein dediziertes Stück Software) übernimmt die Logik des Spiels, während die Clients im Grunde nur die Anzeige übernehmen und Eingaben vom Spieler weiterleiten. (Es ist etwas komplizierter, dazu aber gleich mehr.) Der Tastendruck muss also vom Client entgegengenommen und an den Server gesendet werden. Dieser validiert die Eingabe und sendet dann die darauf folgende Reaktion an alle Spieler (auch an den, der sie gesendet hat).</p>
<p>Um die Latenz zu berechnen, werden die letzten X Befehle mit Zeitstempel und deren Antwort (ebenfalls mit Zeitstempel) in einem Buffer gespeichert. Vergleicht man die Zeitstempel und bildet den Mittelwert aus allen, ergibt sich so die Latenz. Unter Gamern ist oft die Rede vom <q>Ping</q>, was eigentlich der Latenz entspricht.</p>
<p>Doch auch mit dem schnellsten Netzwerk wäre der Spielspaß sehr begrenzt, da bei jedem Tastendruck ein kleines Delay entstehen würde. Um dieses Delay zu eliminieren wurde eine Technik mit dem Namen <strong>Client Side Prediction</strong> entwickelt. Dabei führt der Client die Aktion sofort aus und sendet dann den Befehl zum Server. Braucht der Server zu lange für eine Antwort, berechnet der Client mit Hilfe der letzten Antworten vom Server eine mögliche nächste Position anhand der vergangenen Werte durch Extrapolation. Das gleiche Prinzip gilt natürlich auch für alle anderen Spieler. Diese muss der Client ebenfalls durch Extrapolation berechnen.</p>
<p>Kommt ein neues Update vom Server, muss der Game-State vom Server mit dem des vom Client vorgerechneten Game-State vereint werden. Dies wird durch Interpolation der selbstberechneten Werte mit dem neuen Game-State des Servers gemacht. Kam lange kein Update vom Server mehr, kommt es vor, dass der Client unter (sehr) falschen Annahmen das Spiel weiterlaufen ließ und so der Game-State drastischer korrigiert werden muss. Man bemerkt das im Spiel dann, wenn Mitspieler sprunghaft die Position wechseln. Das berühmte <q>Lag</q>.</p>
<p>Besonders interessant bei einem FPS Spiel sind Latenz-kritische Events wie bspw. die Schüsse auf die Gegner/Mitspieler. Der Spieler hat immer den Zustand von sich selbst in der <strong>Gegenwart</strong>, aber den Zustand der Welt und der anderen Spieler aus der <strong>Vergangenheit</strong>. Schießt der Spieler auf einen Gegner, ist dieser vielleicht schon lange hinter einer Mauer. Um dieses Problem zu lösen schickt der Client in regelmäßigen Abständen seinen kompletten Game-State an den Server. Der Server kennt also den Game-State aller Clients zu jedem Zeitpunkt und kann so die Sicht der Clients rekonstruieren. Hat also der Spieler einen Gegner zwar getroffen, aber auf Basis falscher Daten (da er ja mit den Daten aus der Vergangenheit arbeitet und sich die aktuelle Position usw. selbst berechnet), kann der Server dies dennoch als Treffer werten. Denn der Client hat ja den Gegner getroffen, auch wenn dies nicht der Wahrheit entspricht. Diesen Effekt bemerkt man manchmal, wenn man von einem Gegner noch getroffen wird, obwohl man bereits außerhalb des Schussfeldes steht oder sich bereits hinter eine Wand bewegt hat.</p>
<p>Soviel zur Theorie. Im nächsten Teil werden wir anfangen eine eigene Map zu bauen. Dabei werden wir uns zuerst die Quake-Engine ansehen wie sie Modifikationen am Spiel erlaubt und anschließend den Map-Editor anwerfen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/249
2016-04-20T20:20:00+02:00
2016-04-20T20:20:00+02:00
Elektronik: Emacs Keypad
<p>Ok, ich gebe zu, als jahrelanger <a href="http://vim.org/">Vim</a>-User wird man in <a href="https://www.gnu.org/software/emacs/">Emacs</a> schnell von den langen Shortcuts erschlagen. Sie wirken auf den ersten Blick unglaublich umständlich und unmöglich zu merken. Dennoch folgen sie einem Schema. Manche Funktionen/Shortcuts verwende ich allerdings so selten, dass ich sie mir trotzdem nicht merken kann und sie auf PostIts vor meinem PC kleben habe. Für die PostIts habe ich mir eine Alternative gebaut.</p>
<p>Aus ein paar China-Komponenten lässt sich einfach ein USB-Keyboard bauen, das per Tastendruck Shortcuts und Zeichenfolgen senden kann. Mein erster Probeaufbau habe ich mit einem Arduino Nano, ein paar Widerständen, zwei Zehnerdioden und der <a href="https://www.obdev.at/products/vusb/index.html">VUSB-Bibliothek</a> gemacht.</p>
<p><img src="/images/emacs-keypad1.png" alt="emacs keypad"></p>
<p>Leider hat das alles nicht so funktioniert wie es sollte. Doch mit <a href="http://klautesblog.blogspot.de/">Kai's</a> scharfem Blick und seinem Oszilloskop hatte er meinen Fehler gleich gefunden. Zu meinem Glück hatte er zuvor schon <a href="http://klautesblog.blogspot.de/search/label/LEDCube">ein</a> <a href="http://klautesblog.blogspot.de/search/label/HackStick">paar</a> Projekte mit der VUSB-Lib gemacht.</p>
<p>Danach versuchte ich mich mit der <a href="http://playground.arduino.cc/Code/Keypad">Keypad-Lib</a> von Arduino. Diese funktionierte allein sehr gut, doch in Verbindung mit der VUSB-Lib hatte ich kein Glück. Viele Stunden habe ich versucht, beide Komponenten zu vereinen, doch ohne Glück. Verschiedenste Timing-Probleme und unvorhersehbares Verhalten ohne ersichtlichen Grund beschäftigten mich. Zeitweise verweigerte sogar ein USB-Port meines Notebooks den Dienst.</p>
<p>Auch hier hat mir Kai weitergeholfen und eine <a href="https://git.okoyono.de/klaute/avr-keymatrix">Key-Matrix Lib</a> programmiert (blind und ohne Compiler wohlgemerkt!). Mit dieser Lib funktionierte es super. Vielen Dank nochmal dafür!</p>
<p><img src="/images/emacs-keypad2.png" alt="emacs keypad"></p>
<p>Aufgebaut habe ich alles auf billigem Lochraster und Fädeldraht. Den Rest der Komponenten hab ich schwebend verlötet. Einige Lötstellen habe ich bei der Fehlersuche drei oder vier mal wieder aufgemacht. Deshalb sieht alles etwas chaotisch aus. Mini-USB Buchsen gibt es leider nur in SMD-Bauform, deshalb habe ich die Buchse mit Epoxy in eine Aussparung auf der Platine geklebt und dann mit Fädeldraht nach oben geführt.</p>
<p><img src="/images/emacs-keypad3.png" alt="emacs keypad"></p>
<p>Als Gehäuse wollte ich einmal was neues ausprobieren. Holz sieht gut aus, doch ist leider lange nicht so stabil wie ein Gehäuse aus PLA oder ABS. Evtl. drucke ich mir dafür noch ein Gehäuse.</p>
<p><img src="/images/emacs-keypad4.png" alt="emacs keypad"></p>
<p>Die Tasten selbst haben kleine transparente Kappen, unter die man ein Stück Papier klemmen kann. So kann man die Tasten individuell beschriften und nachträglich anpassen. Kostenpunkt für alles: ca. 7 EUR.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/247
2016-04-14T20:00:00+02:00
2016-04-14T20:00:00+02:00
Im Detail erklärt: Arch Linux Pakete erstellen und Maintainer werden
<p>Eine Linux-Distribution wird von vielen freiwilligen Helfern stets aktualisiert und erweitert. <a href="https://www.archlinux.org/">Arch Linux</a> hat mit <a href="https://aur.archlinux.org/">AUR</a> -- dem Arch User Repository -- einen meiner Meinung nach sehr guten Weg gefunden, wie sich jeder daran beteiligen kann. Ein Paket für Arch Linux zu erstellen und zu betreuen ist nicht schwer und erfordert nur ein bisschen Disziplin.</p>
<p>Bei Arch Linux gibt es die <a href="https://www.archlinux.org/people/developers/">Entwickler</a>, die das Kernteam der Distribution bilden. Sie entwickeln das Framework, treffen Entscheidungen und verwalten die <strong>core</strong> Pakete. Dann gibt es die <a href="https://www.archlinux.org/people/trusted-users/">Trusted User</a> (TU), die Pakete in <strong>extra</strong> und <strong>community</strong> pflegen. Wer nicht zu diesem ausgewählten Kreis gehört, kann dennoch Pakete im AUR erstellen und betreuen. Die Pakete können durch Voting in <strong>community</strong> und dann evtl. sogar nach <strong>extra</strong> wandern, wenn sie gut betreut werden und viele User ihre Stimme dafür gegeben haben.</p>
<p>Ein solches Paket wollen wir bauen. Ich habe mir das Tool <a href="https://github.com/Uzebox/uzebox/tree/master/tools/packrom">packrom</a> ausgesucht, da ich es für ein kommendes Projekt benötige und es dazu noch kein Paket gibt.</p>
<p>Ein AUR Paket ist grundlegend eine Datei, die beschreibt woher der Sourcecode kommt, wie er zu compilieren ist, welche Abhängigkeiten er hat und welche Dateien wo im System untergebracht werden müssen. Diese Datei wird <a href="https://wiki.archlinux.org/index.php/PKGBUILD">PKGBUILD</a> genannt und muss nach einem bestimmten Schema aufgebaut werden. Dazu aber später mehr. Zuerst müssen wir uns mit der Software vertraut machen, die wir zu einem Paket schnüren wollen. Wir brauchen einige Informationen.</p>
<h2 id="projekt-erkunden">Projekt erkunden</h2>
<p>Das <a href="https://github.com/Uzebox/uzebox/tree/master/tools/packrom">Projekt packrom</a> liegt auf Github in einem Unterverzeichnis. Daher können wir die Download-URL schon mal herausfinden. Da es ein Git-Repository ist, müssen wir sicherstellen, dass der User immer die selbe Version bekommt, egal wie weit die Entwicklung fortgeschritten ist. Wir suchen uns deshalb den letzten Commit heraus, der in diesem Ordner gemacht wurde. Das geht auf der Github-Seite ganz bequem. Die Download-URL ist für uns also <code>git+https://github.com/Uzebox/uzebox.git#commit=7818bc4</code>. Die Versionsnummer <strong>1.3</strong> können wir aus dem <a href="https://github.com/Uzebox/uzebox/blob/master/tools/packrom/packrom.cpp#L27">Header des Sourcecodes</a> entnehmen.</p>
<p><img src="/images/aur1.png" alt="aur"></p>
<p>Weiter geht es mit der Lizenz. Diese Angabe ist extrem wichtig und muss korrekt angegeben werden. Im <a href="https://github.com/Uzebox/uzebox/blob/master/tools/packrom/packrom.cpp#L5">Sourcecode</a> steht sie glücklicherweise. Bei den meisten anderen Projekten gibt es eine <strong>LICENSE</strong> Datei, in der die Lizenz beschrieben ist. Dann benötigen wir noch die erforderlichen Abhängigkeiten, die für diese Software erfüllt werden müssen. Hierzu ist nichts zu finden (steht meist in der README oder INSTALL), und auch beim Überfliegen des Sourcecodes konnte ich nichts erkennen. Also können wir davon ausgehen, dass wir keine Abhängigkeiten haben.</p>
<h2 id="trockentest">Trockentest</h2>
<p>Als nächstes müssen wir herausfinden, wie die Software compiliert wird. Ein Blick in das <a href="https://github.com/Uzebox/uzebox/blob/master/tools/packrom/Makefile">Makefile</a> verrät, dass wir mit <code>make help</code> eine Übersicht der Build-Möglichkeiten erhalten. Für uns relevant ist <code>make release</code>. Die Compiler-Flags können wir bei so einem kleinen Projekt ignorieren. Nach dem Ausführen erhalten wir ein einziges Binary, mit dem Namen <code>packrom</code>. Der Compile-Vorgang ist also mehr als trivial.</p>
<h2 id="pkgbuild">PKGBUILD</h2>
<p>Erstellen wir nun unser Paket mit der <code>PKGBUILD</code>-Datei. Dazu legen wir ein neues Verzeichnis mit dem Namen <code>packrom</code> an und erstellen dort die <code>PKGBUILD</code> Datei (eine Bash-Datei):</p>
<pre><code># Maintainer: Aaron Fischer <mail@aaron-fischer.net>
pkgname=packrom
pkgver=1.3
pkgrel=1
pkgdesc='Uzebox(tm) ROM utility'
arch=('any')
license=('GPL3')
url='https://github.com/Uzebox/uzebox'
depends=()
makedepends=()
source=('git+https://github.com/Uzebox/uzebox.git#commit=7818bc4')
md5sums=('SKIP')
build() {
cd "${srcdir}/uzebox/tools/packrom/"
make release
}
package() {
install -Dvm755 "${srcdir}/uzebox/tools/packrom/packrom" "${pkgdir}/usr/bin/packrom"
}
</code></pre>
<p>Der Kommentar am Anfang ist eine Konvention und enthält den Namen des aktuellen Maintainers und aller Contributors. Ich trage dort alle Namen ein, die am Paket durch Verbesserungsvorschläge, Bug-Reports oder Anpassungen mithelfen. Darunter kommen einige Variablen. Hier gibt es noch eine Menge mehr, doch dies ist das minimale Set:</p>
<ul>
<li><code>pkgname</code>: Der Name des Pakets. Dieser muss auf AUR eindeutig und noch nicht vergeben sein.</li>
<li><code>pkgver</code>: Die Version des Pakets.</li>
<li><code>pkgrel</code>: Die <q>Meta-Version</q> des Pakets. Ein Beispiel: Wir erstellen das Paket in der Version 1.3. Nach 2 Wochen finden wir einen Fehler im Paket selbst und korrigieren diesen. Dann inkrementieren wir die <code>pkgrel</code> Nummer um eins, um ein Update bei den Usern zu triggern. Wird eine neue Version released, wird <code>pkgrel</code> wieder auf 1 gesetzt.</li>
<li><code>pkgdesc</code>: Eine einzeilige Beschreibung des Pakets, die dann auch auf der Webseite angezeigt wird.</li>
<li><code>arch</code>: Die <a href="https://wiki.archlinux.org/index.php/Arch_packaging_standards#Architectures">verschiedenen Architekturen</a>, auf denen diese Software lauffähig ist.</li>
<li><code>license</code>: Die Lizenz der Software. Hier lohnt sich ein Blick in <code>/usr/share/licenses/common</code> für eine Übersicht. </li>
<li><code>url</code>: Die Web-Präsenz der Software. Da wir hier keine richtige Webseite haben, habe ich hier die des Git-Repos angegeben.</li>
<li><code>depends</code> und <code>makedepends</code>: Die Abhängigkeiten des Pakets und die Abhängigkeiten für den Buildprozess. (Das Paket <code>base-devel</code> muss nicht extra angegeben werden.)</li>
<li><code>source</code>: Die <a href="https://wiki.archlinux.org/index.php/VCS_package_guidelines#VCS_sources">Download-URL</a> für die Software.</li>
<li><code>md5sums</code>: Die MD5 Summe der Quelle. Mit <code>SKIP</code> kann dieser Schritt übersprungen werden. In unserem Fall ist das notwendig.</li>
</ul>
<p>Weitere Variablen sind <a href="https://wiki.archlinux.org/index.php/PKGBUILD">hier</a> zu finden. Die beiden Funktionen <code>build()</code> und <code>package()</code> sind eigentlich selbsterklärend. In der <code>build()</code> Funktion sind alle nötigen Schritte aufgeführt, die zum Compilieren der Software in <code>$srcdir</code> benötigt werden. Die <code>package()</code> Funktion baut das Paket im Zielverzeichnis <code>$pkgdir</code> zusammen. In unserem Fall ist dies nur eine einzelne Datei, die wir mit dem <code>install</code> Befehl an die entsprechende Stelle kopieren können.</p>
<p>Die Philosophie (im Kontrast du Ubuntu) ist, die Software so nah am Ursprungszustand zu belassen wie möglich. Also keine unnötigen Patches, Config-Files oder ähnliches einbauen. Kann etwas nicht compiliert werden oder ist absolut nicht Kompatibel, ist der Entwickler (upstream) die erste Anlaufstelle. Entweder über den Bug-Tracker oder direkt die E-Mail Adresse aus dem Sourcecode suchen und den Entwickler anschreiben. Die Paket-Maintainer sind meist die ersten, die mit einer neuen Version in Berührung kommen.</p>
<h2 id="testen-und-bauen">Testen und bauen</h2>
<p>Erstellen wir also unser neues Paket mit dem Tool <code>makepkg</code>:</p>
<pre><code>makepkg -f
</code></pre>
<p>Es werden zwei Verzeichnisse <code>src</code> und <code>pkg</code> angelegt. Diese enthalten den Sourcecode und das fertige Paket. Hier kann man noch einmal prüfen, ob alles korrekt war. Lief alles erfolgreich durch, können wir noch <code>namcap</code> nutzen, um unser Paket auf die üblichen <q>Schwachstellen</q> zu testen, die nicht den <a href="https://wiki.archlinux.org/index.php/Creating_packages">Package Guuidelines</a> entsprechen.</p>
<pre><code>namcap PKGBUILD
</code></pre>
<p>Passt alles, können wir zum letzten Schritt übergeben. Es gibt eine sehr kontroverse Diskusion darüber, warum die Arch Linux Pakete ein Bash-File (PKGBUILD) verwenden, um das Paket zu definieren. Zum einen ist dies sehr flexibel, was extrem praktisch ist, da jede Software anders ist. Anderseits lässt sich ein Bash-File sehr schwer parsen, um die nötigen Informationen herauszufinden. Variablen können dynamisch zusammengebaut werden, in einem <code>if</code> stecken etc. Deshalb gibt es das Tool <code>mksrcinfo</code>, das eine <code>.SRCINFO</code> Datei erstellt, welche ein parserfreundliches Format hat. Macht <code>mksrcinfo</code> einen Fehler, kann man dies dann in der <code>.SRCINFO</code> Datei anpassen. In unserem Fall sollte dies aber keinerlei Probleme geben:</p>
<pre><code>mksrcinfo
</code></pre>
<p>Unser Paket ist somit fertig und bereit für den Upload.</p>
<h2 id="deployment">Deployment</h2>
<p>Grundvoraussetzung ist der Besitz <a href="https://aur.archlinux.org/packages/?SeB=m&K=aaronfischer">eines Accounts</a> auf <a href="https://aur.archlinux.org/register/">AUR</a>. Hier hinterlegen wir in den <a href="https://aur.archlinux.org/account/aaronfischer/edit/">Einstellungen</a> den eigenen Public-Key und konfigurieren folgendes in unserer <code>~/.ssh/config</code>:</p>
<pre><code>Host aur.archlinux.org
IdentityFile ~/.ssh/aur
User aur
</code></pre>
<p>Ist das gemacht, können wir das Deployment starten. Seit ca. einem Jahr lässt sich dies ganz bequem über Git machen und man muss sich nicht mehr mit Web-Formularen herumschlagen.</p>
<pre><code>git init .
git remote add origin git+ssh://aur@aur.archlinux.org/packrom.git
git fetch origin
git add PKGBUILD .SRCINFO
git commit -m "Package creation in version 1.3"
git push --set-upstream origin master
</code></pre>
<p>Danach ist unser Paket direkt <a href="https://aur.archlinux.org/packages/packrom/">live</a>. Auf der Webseite lassen sich dann noch ein paar Tags vergeben. Das Paket kann dann wie gewohnt mit <code>yaourt</code> oder einem anderen Paketmanager-Interface installiert werden:</p>
<pre><code>yaourt packrom
</code></pre>
<h2 id="maintaining">Maintaining</h2>
<p>Ein letzter wichtiger Schritt fehlt aber noch: Das Paket ist nun in unseren Händen. Wenn die Entwickler eine neue Version herausbringen, muss auch unser Paket aktualisiert werden -- und das möglichst schnell. Deshalb ist es ratsam, sich geeignete <q>Push-Notifications</q> einzurichten. Bei Github ist die <q>Watch</q>-Funktion praktisch. Für Software die noch auf SourceForge liegt, kann eine E-Mail Notification eingerichtet werden. Auch empfehlenswert sind die <q>devel-</q> oder <q>release-</q> Mailinglisten, um am Ball zu bleiben. AUR-User können ebenfalls das Paket mit einem <q>Out of Date</q>-Flag versehen. Hier wird man ebenfalls per E-Mail benachrichtigt. Für statische Seiten gibt es diverse Online-Tools, die eine E-Mail Benachrichtigung schicken wenn sich der Inhalt der Seite ändert. Oder man bittet die Entwickler um eine kurze Info-Mail bei einer neuen Version.</p>
<p>Was also tun bei einer neuen Version? Die PKGBUILD muss angepasst werden, aber wie?</p>
<ul>
<li>Changelog lesen</li>
<li>Abhängigkeiten neu prüfen</li>
<li>Versionsnummer rausfinden</li>
<li>Hat sich der Build-Prozess geändert?</li>
<li>Müssen andere Dateien kopiert/installiert werden?</li>
</ul>
<p>Passt wieder alles, muss die <code>.SRCINFO</code> Datei wieder aktualisiert werden:</p>
<pre><code>mksrcinfo
</code></pre>
<p>Danach alles Commiten und pushen.</p>
<pre><code>git add PKGBUILD .SRCINFO
git commit -m "What the change does"
git push
</code></pre>
<p>Ich hoffe, ich konnte dem ein oder anderen Mut machen, selbst ein Paket für AUR (oder eine andere Distribution!) zu erstellen. Wer sich das nicht traut kann auch ein <a href="https://aur.archlinux.org/packages/?O=0&SeB=nd&K=&outdated=&SB=n&SO=a&PP=50&do_Orphans=Orphans">Paket ohne Maintainer (Orphans)</a> <q>adoptieren</q> und sich zukünftig darum kümmern. Aktuell sind 1233 Pakete in AUR ohne Maintainer. Einfach auf <q>Adopt Package</q> im rechten Kasten klicken. Viel Erfolg!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/246
2016-04-05T21:16:00+02:00
2016-04-05T21:16:00+02:00
Im Detail erklärt: Einfache künstliche neuronale Netze
<p>Nach <a href="https://deepmind.com/alpha-go.html">AlphaGo</a> hat wohl jeder schon mal den Begriff <em>Neuronales Netz</em> oder <em>Deep Learning</em> gehört. Sei es aus Angst vor den Maschinen mit Gehirn oder aus Begeisterung vor der Technik und den Möglichkeiten (oder eine Mischung aus beidem). Doch sind neuronale Netze schon lange im Einsatz, etwa bei der Post um handgeschriebene Adressen auf Briefen zu lesen oder die Google Spracherkennung. Politiker/Parteien wählen mit neuronalen Netzen die <a href="http://arxiv.org/pdf/1309.2183.pdf">ideale Zielgruppe</a> aus. Computer können nun sogar <a href="https://www.youtube.com/watch?v=SCE-QeDfXtA">träumen</a>. Sie sind mittlerweile allgegenwärtig. Grund genug, sich das mal genauer anzuschauen.</p>
<p>Ich werde dabei allerdings nur die absoluten Grundlagen aufzeigen. Wer das Thema spannend findet und mehr darüber lernen möchte, sollte sich das eBook <a href="http://neuralnetworksanddeeplearning.com/">Neural Networks and Deep Learning</a> ansehen.</p>
<p>Ein künstliches neuronales Netzwerk besteht aus einem oder mehreren Neuronen. Neuronen sind Verknüpfungen, die bei bestimmten Reizen (Inputs) <q>gefeuert</q> werden. Um die Sache so einfach wie möglich zu halten werde ich das vereinfachte Modell eines <a href="https://de.wikipedia.org/wiki/Perzeptron">Perzeptrons</a> verwenden. Das grundlegende Prinzip bleibt aber das gleiche.</p>
<p><img src="/images/perceptron.png" alt="perceptron"></p>
<p>Ein Perzeptron erwartet einen oder mehrere Inputs. Diese haben unterschiedliche Gewichte -- also wie wichtig jeder einzelne Input zu werten ist. Das Perzeptron selbst entscheidet dann anhand einer Aktivierungsfunktion, ob es <q>feuert</q> oder nicht. Im konkreten Fall ob es eine 1 oder eine 0 ausgibt. Die Logik für die Entscheidung sieht wiefolgt aus:</p>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
});
</script>
<script type="text/javascript" async src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML-full"></script>
<p>$$output =
\begin{cases}
0, & \text{if $\sum_j w_jx_j$ $\le$ threshold} \\
1, & \text{if $\sum_j w_jx_j$ $\gt$ threshold}
\end{cases}$$</p>
<p>Es wird die Summe aus den Inputs $x_1...x_n$ mal den Gewichten $w_1...w_n$ berechnet und anschließend mit einem Schwellwert $threshold$ verglichen. Wird der Schwellwert überschritten, <q>feuert</q> das Perzeptron, wenn nicht bleibt es aus und gibt 0 zurück. Je höher die Gewichtung des Inputs, desto eher wird das Perzeptron aktiviert wenn dieser Input groß ist. Die Gewichtung gibt so die <q>Wichtigkeit</q> des Inputs an. Ein künstliches neuronales Netz besteht aus mehreren solcher Perzeptrons untereinander und hintereinander. Der Output des einen ist der Input des anderen. So bildet sich ein ganzes Netz.</p>
<p>Doch wie kann nun ein solches Konstrukt selbständig lernen? Der Knackpunkt sind die Gewichte und der Schwellwert. Dies sind variable Werte, die so lange justiert werden können, bis man das gewünschte Ergebnis erhält (überwachtes Lernen). Das Netz muss also erst lernen, je mehr desto besser. Dazu werden Beispieldaten in das Netz als Inputs eingespielt und anschließend die Outputs mit dem Sollwert verglichen. Weicht der Output dem gewünschten Output ab, wird an den Gewichten und dem Schwellwert gedreht. Konkret heißt das:</p>
<ul>
<li>Ist der Output 1 und der erwartete Output 1, wird nichts verändert</li>
<li>Ist der Output 0, doch der erwartete Output soll 1 sein, werden die Gewichte vergrößert</li>
<li>Ist der Output 1, doch der erwartete Output soll 0 sein, werden die Gewichte verkleinert</li>
</ul>
<p>Vor dem ersten Durchlauf werden die Gewichte auf einen zufälligen Wert gesetzt. Um wie viel die Gewichte verkleinert oder vergrößert werden bestimmt auch die Lerngeschwindigkeit des Netzes. Ist die Zahl sehr klein, dauert es ewig bis sie passend eingestellt sind. Ist die Zahl zu groß, kann es sein, dass das Netz nie richtig eingestellt werden kann da es bei jedem verkleinern/vergrößern über das Ziel hinausschießt.</p>
<p>Dies macht man nun so lange bis das Ergebnis die gewünschte Genauigkeit hat. Gibt man nun dem Netz einen bis jetzt unbekannten Input, kann es mit hoher Wahrscheinlichkeit einen passenden Output berechnen. Das ist das Ziel.</p>
<p>Dieses Konzept können wir nun in (Ruby)-Code ausdrücken. Ich habe hier ein <strong>OR</strong> aus einem einzigen Perceptron mit zwei Inputs gebaut. Mit den Trainingsdaten werden in 100 Durchläufen die Gewichte ermittelt.</p>
<pre><code>#!/usr/bin/env ruby
require 'matrix'
weights = Vector[rand(3), rand(3)]
learning_rate = 0.1
threshold = 2
# Training data
data = [[Vector[0, 0], 0],
[Vector[0, 1], 1],
[Vector[1, 0], 1],
[Vector[1, 1], 1]]
# Training
100.times.each do
inputs, expected_output = data.sample
output = inputs.inner_product(weights) > threshold ? 1 : 0
weights = weights.map do |weight|
if output == 0 and expected_output == 1
weight+learning_rate
elsif output == 1 and expected_output == 0
weight-learning_rate
else
weight
end
end
end
# Usage
data.each do |datum|
inputs, expected_output = datum
output = inputs.inner_product(weights) > threshold ? 1 : 0
puts "#{inputs} -> #{expected_output}: #{output}"
end
</code></pre>
<p>Einige Stellen hätte man noch vereinfachen können, habe ich aber zum besseren Verständnis so gelassen. Der Aufruf des Scripts gibt folgendes aus:</p>
<pre><code>Vector[0, 0] -> 0: 0
Vector[0, 1] -> 1: 1
Vector[1, 0] -> 1: 1
Vector[1, 1] -> 1: 1
</code></pre>
<p>Um das Netz noch mächtiger zu machen, gibt es einige Optimierungen. In unserem Beispiel haben wir die Stufenfunktion verwendet, die entweder 0 oder 1 zurückgibt. Tauscht man nun diese durch die <a href="https://de.wikipedia.org/wiki/Sigmoidfunktion">Sigmoidfunktion</a> aus, hat man keinen binären Output mehr sondern eine Fließkommazahl zwischen 0 und 1. Damit lassen sich viel komplexere Probleme bearbeiten und das Netz arbeitet besser. Am Ende muss dann natürlich das Ergebnis wieder (durch Zuhilfenahme eines Schwellwerts) zu einer 1 oder 0 umgeformt werden.</p>
<p>Wer die Grundlagen verstanden hat, kann dann eine der vielen Bibliotheken nutzen, die wie ein Baukasten für neuronale Netze verwendet werden können. Mit ein paar Zeilen Code lassen sich schon sehr mächtige Dinge bauen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/245
2016-04-01T23:30:00+02:00
2016-04-01T23:30:00+02:00
Programme und OpenSource: SCM Timeline Poster
<p>2012 hatte ich für einen Vortrag ein Poster angefertigt, das die verschiedenen Versionskontrollsysteme und deren Vorläufer aufzeigt. Zu dieser Zeit befanden sich einige konkurrierende Produkte im direkten Popularitätskampf. Verteilte Systeme ohne zentralen Server gewannen immer mehr an Aufmerksamkeit. Vier Jahre später hat sich die Aufregung gelegt. Der Kampf ist weitestgehend entschieden. Deshalb habe ich die Grafik aktualisiert.</p>
<p>Schon 2012 zeichnete sich ab, dass proprietäre Systeme (blau) keiner besonders glorreichen Zukunft entgegenblicken werden. Prominentestes Beispiel war wohl Bitkeeper. Nachdem sie das Lizenzmodell geändert hatten, haben sich zwei Open Source Produkte Git und Mercurial gebildet. Der klägliche Versuch mit einer <q>Free Use</q>-Version wieder aus der Misere herauszukommen scheiterte schnell.</p>
<p>Das Rennen zwischen Mercurial, Git und Bazaar war im vollen Gange. Alle drei Systeme waren vom Funktionsumfang ähnlich. SVN war 2012 immer noch Allgegenwärtig.</p>
<p><img src="/images/scm-history-2012.png" alt="scm timeline"></p>
<p>Vier Jahre später hat Git das Rennen gewonnen -- vermutlich der Verdienst von GitHub und Linus Torvalds. Mercurial ist weiterhin in Python-Kreisen beliebt, doch hat es nicht die Verbreitung die Git in den letzten Jahren erlebt hat. SVN wird zunehmend unwichtig und ist eigentlich nur noch aus legacy-Gründen da. Als einzige ernstzunehmende kommerzielle Lösung ist noch Perforce im Rennen und wird hauptsächlich im Industrie/Automotive-Bereich eingesetzt.</p>
<p><img src="/images/scm-history-2016.png" alt="scm timeline"></p>
<p>Wer sich ein Stück Geschichte an die Wand hängen möchte, kann sich die Grafik bei einem Poster-Druck-Service seiner Wahl für ein paar EUR drucken lassen. Die Grafiken sind mit Inkscape gemacht und stehen unter der Creative Commons Lizenz. Farben anpassen, Fehler korrigieren oder die Grafiken für etwas ganz anderes verwenden ist also kein Problem.</p>
<ul>
<li>Version 2012: <a href="/files/scm-history-2012.png">png (600dpi)</a>, <a href="/files/scm-history-2012.svg">Inkscape SVG</a></li>
<li>Version 2016: <a href="/files/scm-history-2016.png">png (600dpi)</a>, <a href="/files/scm-history-2016.svg">Inkscape SVG</a></li>
</ul>
<p><strong>Anmerkung:</strong> Die Informationen für die Poster stammen aus meinen eigenen Recherchen. Ich habe nur die mir als relevant erscheinenden Daten verwendet. Ungenauigkeiten oder gar Fehler können also enthalten sein. Ich habe nur die bekanntesten Versionskontrollsysteme aufgeführt, es ist also kein 100% akkurates Abbild der Wirklichkeit.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/239
2016-03-26T21:30:00+01:00
2016-03-26T21:30:00+01:00
Elektronik: Deine eigene LISP Machine
<p>Wer wollte nicht schon immer mal eine Lisp-Maschine mit einem echten <a href="https://en.wikipedia.org/wiki/Real-time_operating_system">RTOS</a> sein Eigen nennen? Wenn möglich schön handlich, mit Wifi, serieller Console über USB und kosten darf es auch nicht mehr als 5 EUR. Mit dem beliebten <a href="http://espressif.com/products/hardware/esp8266ex/overview">ESP8266</a> von espressif ist das tatsächlich möglich.</p>
<p>Jonas Karlsson (yesoc) hat mit seinem Projekt <a href="https://github.com/yesco/esp-lisp">esp-lisp</a> einen Lisp Interpreter für den ESP8266 geschrieben. Der Funktionsumfang ist für sein Beta-Stadium schon beträchtlich. Die Lisp-Implementierung ist bereits gut ausgereift und bietet schon rudimentäre Interaktion mit dem ESP8266-Board selbst (IO-Pins, WiFi).</p>
<p>Da der ESP8266 nicht direkt an USB angeschlossen werden kann, empfiehlt es sich, ein fertiges Dev-Board wie das <a href="http://de.aliexpress.com/item/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/32529101036.html">Wemos D1 Mini</a> oder das <a href="http://de.aliexpress.com/item/New-Wireless-module-CH340-NodeMcu-V3-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/32549862093.html">CH340 NodeMCU V3</a> zu verwenden. Diese können direkt mit einem USB-Kabel an den PC angeschlossen werden.</p>
<p><img src="/images/esp8266.jpg" alt="ESP8266"></p>
<h2 id="esp-sdk-compilieren">ESP SDK compilieren</h2>
<p><strong>Achtung</strong>: Alle Befehle müssen in einer <code>bash</code> Shell ausgeführt werden.</p>
<p>Als erstes werden wir das ESP Open SDK herunterladen und compilieren. Es enthält ein paar Hilftfunktionen, die wir für die weiteren Schritte benötigen. Evtl. müssen noch ein paar Pakete wie <code>gperf</code> oder <code>make</code> installiert werden. Bei mir war noch die Umgebungsvariable <code>$LD_LIBRARY_PATH</code> gesetzt, diese musste ich zuvor noch leeren. Vor dem compilieren muss (Stand 15.03.2016) noch eine Datei <a href="https://github.com/pfalcon/esp-open-sdk/issues/138#issuecomment-192681746">gepatched</a> werden, damit der Compilevorgang abgeschlossen werden kann.</p>
<pre><code>git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk
make STANDALONE=n
</code></pre>
<p>Im Anschluss muss die <code>$PATH</code> Variable angepasst werden, so dass die darauffolgenden Befehle die gerade compilierten Tools finden. Es lohnt sich, alle folgenden Komponenten/Projekte in einem Unterverzeichnis auszuchecken. Am Ende sollten drei Unterverzeichnisse vorhanden sein. In diesem Unterverzeichnis muss zudem eine Datei mit dem Namen <code>path-add-esp</code> angelegt und mit folgendem Inhalt gefüllt werden:</p>
<pre><code>export PATH=/home/aaron/workbench/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
</code></pre>
<h2 id="rtos-esp8266-sdk-beschaffen">RTOS ESP8266 SDK beschaffen</h2>
<p>Im Anschluss benötigen wir das auf <a href="http://www.freertos.org/">FreeRTOS</a> basierende Framework für den ESP8266. Dies ist die Basis für esp-lisp. Hiervon benötigen wir hauptsächlich die Source-Files, compilieren müssen wir hier nichts. Dies folgt im nächsten Schritt.</p>
<pre><code>cd ..
git clone --recursive https://github.com/SuperHouse/esp-open-rtos
</code></pre>
<p>Eine kleine Anpassung müssen wir aber auch hier machen. In der Datei <code>include/ssid_config.h</code> muss die Zeile mit dem <code>#warning</code> entfernt werden. Die WiFi-Daten müssen wir nicht angeben.</p>
<h2 id="esp-lisp-compilieren">esp-lisp compilieren</h2>
<p>Zuletzt brauchen wir noch den esp-lisp Code selbst. Und auch hier müssen wir eine kleine Änderung vornehmen, wenn <code>python</code> in Version 3.x installiert ist (bei ArchLinux der Fall). Hierzu einfach die Datei <code>xtensa-lx106-elf/bin/esptool.py</code> öffnen und in der ersten Zeile <q>python</q> durch <q>python2.7</q> austauschen. Das praktische Script <code>run</code> erledigt dann den Rest für uns. Mit <code>make flash</code> kann dann zuletzt die Firmware auf den ESP8266 aufgespielt werden (ein an USB angeschlossener ESP8266 vorausgesetzt).</p>
<pre><code>cd ..
git clone https://github.com/yesco/esp-lisp.git
cd esp-lisp
./run
make flash
</code></pre>
<h2 id="starten">Starten</h2>
<p>Die Serielle Verbindung zur Lisp-Maschine kann mit dem Script <code>mcu</code> aufgebaut werden (das Programm <code>screen</code> muss installiert sein).</p>
<pre><code>./mcu
</code></pre>
<p>Nach einem Druck auf <q>Enter</q> wird man mit einer REPL begrüßt.</p>
<pre><code>lisp> (define fib (lambda (n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))
#fib
lisp> (fib 10)
89
</code></pre>
<p>Ziemlich cool ist die trace-Funktion (einschalten mit <code>trace on</code>). Hier kann die Schrittweise Evaluation sehr anschaulich angezeigt werden. Das hilft vor allem beim Erlernen von Lisp.</p>
<pre><code>lisp> trace on
lisp> (fib 2)
---> (fib 2)
ENV
---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
ENV n=2
---> (#< n 2)
ENV n=2
nil <--- (#< n 2)
---> (#+ (#fib (#- n 1)) (#fib (#- n 2)))
ENV n=2
---> (#fib (#- n 1))
ENV n=2
---> (#- n 1)
ENV n=2
1 <--- (#- n 1)
---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
ENV n=1
---> (#< n 2)
ENV n=1
t <--- (#< n 2)
1 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
1 <--- (#fib (#- n 1))
---> (#fib (#- n 2))
ENV n=2
---> (#- n 2)
ENV n=2
0 <--- (#- n 2)
---> (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
ENV n=0
---> (#< n 2)
ENV n=0
t <--- (#< n 2)
1 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
1 <--- (#fib (#- n 2))
2 <--- (#+ (#fib (#- n 1)) (#fib (#- n 2)))
2 <--- (#if (#< n 2) 1 (#+ (#fib (#- n 1)) (#fib (#- n 2))))
2 <--- (#fib 2)
2
</code></pre>
<p>Ich habe für meine LISP-Machine ein kleines Gehäuse <a href="/cah">gedruckt</a>. Im Innern arbeitet ein winziges Dev-Board, das ich mit ein paar Tropfen Sekundenkleber eingeklebt habe. Eine WS2812 RGB-LED, die auf dem Dev-Board praktischerweise aufgelötet ist, beleuchtet das Innere.</p>
<p><img src="/images/lisp-machine1.png" alt="lisp machine"></p>
<p>Bisher ist mein Gehäuse nur grundiert und es fehlt noch die Bemalung und die Aufschriften. Dennoch funktioniert der Winzling schon wie das Vorbild.</p>
<p><img src="/images/lisp-machine2.png" alt="lisp machine"></p>
<p>Erstaunlich, dass vor 30 Jahren solche Maschinen unbezahlbar und groß wie ein Kleiderschrank waren. Heute ist es nur noch Spielzeug und passt zwischen zwei Finger.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/244
2016-03-23T21:15:00+01:00
2016-03-23T21:15:00+01:00
Effektiv programmieren: Clojure Debugging in Emacs
<p>Die Kombination aus <a href="https://www.gnu.org/software/emacs/">Emacs</a> und <a href="https://github.com/clojure-emacs/cider">CIDER</a> ist meiner Meinung nach allem anderen weit überlegen. Debugging mit CIDER macht tatsächlich Spaß und geht einfach von der Hand.</p>
<p>Eine aktuelle Verson von CIDER (0.11+) vorausgesetzt, ist das Debugging von einzelnen Funktionen oder ganzen Funktionalitäten sehr einfach. Einfach an die Funktion, die getestet werden soll, ein <code>#dbg</code> voranstellen. Dies aktiviert den Debugger, wenn die Funktion ausgeführt wird. Mit <code>n</code> kann dann die Evaluation Schrittweise durchgeführt werden. Das Ergebnis wird stets hinter der Funktion angezeigt.</p>
<p><img src="/images/cider-anim2.gif" alt="cider"></p>
<p>Mit <code>i</code> können Ausdrücke an der aktuellen Stelle eingefügt (und direkt evaluiert) werden. Dies ist extrem hilfreich, wenn man verschiedene Inputs testen will, eine Schleife vorzeitig beenden will oder ein Fehlverhalten testen will. Mit <code>l</code> kann man sich zu jeder Zeit alle lokalen Variablen und deren Werte anzeigen lassen.</p>
<p>Ein weiteres cooles Feature von CIDER ist der <q>enlighten-mode</q>. Hier werden allen Symbolen/Variablen (die evaluiert werden) deren aktuellen Werte vorangestellt. Besonders bei komplexeren Funktionen mit vielen Bindings und/oder Verschachtelungen sieht man sofort, welcher Pfad evaluiert wurde und mit welchen Bindings. Die in anderen Programmiersprachen üblichen <code>var_dump</code>/<code>println</code> Debug-Sessions wirken dagegen, als kämen sie aus der Steinzeit.</p>
<p><img src="/images/cider-anim3.gif" alt="cider"></p>
<p>Zum Aktivieren genügt es, ein <code>#light</code> vor ein <code>defn</code> oder <code>def</code> zu schriben.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/243
2016-03-20T19:40:00+01:00
2016-03-20T19:40:00+01:00
Elektronik: ESP8266 als Arduino-Plattform
<p>Ein Arduino (Uno) kostet ca. 20 EUR. Mit dem Arduino Pro Mini für ca. 10 EUR geht es noch etwas billiger. Ein ESP8266 hingegen kostet ca. 2 EUR, als Dev-Board um 4 EUR. Zudem bietet die Alternative noch ein paar mehr Features wie WiFi, I2C an jedem beliebigen Pin oder die höhere PWM-Auflösung. Mit der Arduino IDE lässt sich der ESP8266 dank <a href="https://github.com/esp8266/Arduino">dieses Projekts</a> verwenden als sei es ein Arduino.</p>
<h2 id="installation">Installation</h2>
<p>Bevor der ESP8266 benutzt werden kann, muss es als Board in der Arduino IDE installiert werden. Dazu unter <q>Preferences</q> bei <q>Additional Board Manager URLs</q> folgende URL angeben:</p>
<pre><code>http://arduino.esp8266.com/stable/package_esp8266com_index.json
</code></pre>
<p><img src="/images/esp8266-arduino.png" alt="Arduino IDE"></p>
<p>Anschließend kann unter <q>Tools</q> -> <q>Board Manager ...</q> das neue Board ausgewählt und installiert werden. Das war es auch schon. Nun kann das Board wie gewohnt aus dem Menü ausgewählt werden.</p>
<p><img src="/images/esp8266-arduino2.png" alt="Arduino IDE"></p>
<h2 id="hello-wifi">Hello WiFi</h2>
<p>Zum Testen schreiben wir einen kleinen Webserver, der auf dem ESP8266b läuft und über zwei URLs eine LED an bzw. wieder aus macht. Die <a href="https://github.com/esp8266/Arduino#documentation">Dokumentation</a> ist hier extrem hilfreich. Auch unerlässlich ist die Pinbelegung des verwendeten Boards/Moduls. Hier das für den <a href="http://de.aliexpress.com/item/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/32529101036.html">Wemos D1 Mini</a> (die türkisen Nummern sind die für uns relevanten)</p>
<p><img src="/images/wemosd1mini.jpg" alt="Arduino IDE"></p>
<p>Für unser Beispiel verwenden wir <code>D8</code>, an den wir eine LED gegen Ground anschließen. Wir können im Code entweder <code>D8</code> oder <code>15</code> verwenden.</p>
<p>Der Code selbst ist im Grunde recht simpel. Zu Beginn definieren wir zwei Konstanten für die SSID und das Passwort. In der <code>setup()</code> Funktion wird die WLan-Verbindung aufgebaut und so lange gewartet bis diese steht. Danach wird die vom DHCP-Server zugewiesene IP auf der seriellen Console ausgegeben. Danach wird der Webserver gestartet.</p>
<p>In der <code>loop()</code> Funktion warten wir auf neue Verbindungen. Kommt ein neuer Request, untersuchen wir die erste Zeile, die bei HTTP die Methode (GET, POST, ...) und die URL enthält. Wir interessieren uns nur für die URL, über die wir entscheiden was wir machen. Ist der Substring <q>/on</q> enthalten, schalten wir die LED an D8 an, ist <q>/off</q> enthalten, schalten wir sie aus. Alle anderen URLs ignorieren wir. Zum Schluss senden wir noch eine minimale Response.</p>
<pre><code>#include <ESP8266WiFi.h>
const char* ssid = "ba_nomap";
const char* pass = "sup3rS3cr3t";
WiFiServer server(80);
void setup() {
pinMode(D8, OUTPUT);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.begin(115200);
Serial.println(WiFi.localIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (!client) return;
while (!client.available()) delay(1);
String request = client.readStringUntil('\r');
client.flush();
Serial.print(request);
if (request.indexOf("/on") != -1)
digitalWrite(D8, 1);
else if (request.indexOf("/off") != -1)
digitalWrite(D8, 0);
else {
client.stop();
return;
}
client.print("HTTP/1.1 200 OK\r\n");
client.flush();
}
</code></pre>
<p>Flashen wir nun das Programm auf den ESP8266 und starten es. Es dauert einen Moment, dann sollte eine IP-Adresse auf der seriellen Console auftauchen. Gibt man diese IP, gefolgt von <q>/on</q> im Browser ein, leuchtet die LED auf. Mit <q>/off</q> lässt sich diese wieder ausschalten.</p>
<p>Wie man sieht, alles keine Black Magic, dieses <q>Internet of Things</q> :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/241
2016-03-15T20:20:00+01:00
2016-03-15T20:20:00+01:00
Im Detail erklärt: 3D-Printing mit Clojure
<p>3D-Modelle für den 3D-Druck zu erstellen ist gar nicht so schwer. Auch ohne teure Modelling-Tools lässt sich entspannt arbeiten -- besonders dann wenn Clojure im Spiel ist. Welche Schritte von der Idee zum gedruckten Objekt notwendig sind, will ich hier einmal aufzeigen.</p>
<p>Zu Beginn mache ich ein paar Abmaße und Skizzen vom fertigen Objekt (oder auch Teile davon). Überhänge will man generell beim 3D-Druck vermeiden, da hierfür Support-Material benötigt wird und sie generell schlechter werden. Deshalb macht es Sinn, das Objekt in mehrere Teile zu zerlegen, die dann am Ende zusammengeklebt werden können. So auch in meinem Fall. Ich möchte das Gehäuse eines alten Computersystems nachbauen (Der Hintergrund dazu in einem anderen Post).</p>
<p><img src="/images/clojure-3d-print0.png" alt="Clojure 3d Print"></p>
<p>Die Bibliothek <a href="https://github.com/farrellm/scad-clj">scad-clj</a> ist eine einfache DSL für <a href="http://www.openscad.org/">OpenSCAD</a>. OpenSCAD ist wiederum ein CAD-Modeller, der per Software angesteuert werden kann. Es gibt also Befehle um Formen (Würfel, Zylinder, ...) zu erzeugen und Operationen (verschieben, skalieren, ...) die auf diese angewendet werden können. Die Formen können dann aufeinander angewendet werden (verbinden, Schnittmenge, ...), um komplexere Strukturen zu erzeugen.</p>
<div class="stl-viewer" data-file="/files/lisp-machine-front.stl" data-scale="2.5" width="790px" height="400px"></div>
<p>Mit scad-clj kann diese Funktionalität in Clojure genutzt werden. Die Bibliothek kann dann den erforderlichen OpenSCAD-Code mit <code>write-scad</code> erstellen.</p>
<pre><code>(defn print-scad [filename shape]
(spit (str filename ".scad")
(write-scad
(color [0.6 0.6 0.6] shape))))
</code></pre>
<p>Hat man parallel OpenSCAD mit der generierten Datei offen und einen Haken bei <q>Automatic Reload and Preview</q> gemacht, aktualisiert sich das 3D-Modell direkt nach dem Evaluieren der Funktion.</p>
<p>Hier ist nun der entsprechende Code für die Vorderseite des Objekts. Wie man sieht, sind es im Grunde nicht viele Befehle die gebraucht werden. Die Vorderseite besteht also nur aus ein paar Würfeln (<code>cube</code>), die an die entsprechende Position verschoben/gedreht werden (<code>translate</code>, <code>scale</code>, <code>rotate</code>) und dann aufeinander angewendet werden (<code>union</code>, <code>difference</code>). Es ist erstaunlich, wie weit man mit diesen einfachen Befehlen kommt!</p>
<p>Auch sehr angenehm ist, dass man den vollen Funktionsumfang von Clojure zur Verfügung hat. So beispielsweise bei den Lüftungsschlitzen.</p>
<pre><code>(print-scad
"front"
(binding [*center* false]
(union
(translate [6 25 4] (cube 50 10 2))
(difference
; Base
(cube 65 37 5)
; Input hole
(translate [40 7 -1] (cube 14 16 10 ))
(translate [32 7 5] (rotate (/ pi 7) [0 1 0] (cube 17 16 10 )))
; Venting holes
(apply union
(map #(translate [7 (* % 4) 3] (cube 20 2 5 ))
(range 1 6)))
; Bevels
(translate [-21 -2 -20] (rotate (/ pi 4) [0 -1 0] (cube 40 40 10 )))
(translate [43 -2 20] (rotate (/ pi 4) [0 1 0] (cube 40 40 10 )))))))
</code></pre>
<p>Das so erstellte 3D-Modell sieht dann in OpenSCAD wie folgt aus. Praktisch ist hier, dass man das Objekt drehen, zoomen und von allen Seiten/Blickwinkeln betrachten kann. Dadurch dass sich das Objekt automatisch ändert (und der Kamerawinkel gleich bleibt) wenn der Clojure-Code evaluiert wird, hat man einen sehr entspannten Workflow und muss nur selten den Editor verlassen.</p>
<p><img src="/images/clojure-3d-print1.png" alt="Clojure 3d Print"></p>
<p>Ist das Objekt wie man es gerne hätte, kann es in OpenSCAD als STL-Datei exportiert werden. Diese lässt sich dann mit <a href="http://slic3r.org/">Slic3r</a> für die Weiterverarbeitung öffnen. Slic3r ist ein G-Code Generator, mit dem aus einem 3D-Modell Instruktionen für den 3D-Drucker erstellt. So werden aus Flächen und Formen Bewegungen berechnet.</p>
<p><img src="/images/clojure-3d-print2.png" alt="Clojure 3d Print"></p>
<p>In Slic3r kann nun allerhand eingestellt werden. Zuerst wird die Position des Objekts auf der Druck-Unterlage positioniert. Danach können Einstellungen zum Drucker, dem Filament (dem verwendeten Plastik-Rohstoff) und dem Druck selbst vorgenommen werden. Hier ist viel Experimentieren und Erfahrung angesagt. Diese Einstellungen bestimmen maßgeblich die Qualität und die Dauer des Drucks. So kann beispielsweise festgelegt werden, mit welchem Muster die Flächen gefüllt werden.</p>
<p>Sind alle Einstellungen gemacht, kann der <a href="http://reprap.org/wiki/G-code">G-Code</a> generiert werden. Dies sind einfache Befehle wie <a href="http://reprap.org/wiki/G-code#G92:_Set_Position">G92</a> (fahre an eine bestimmte Position) oder <a href="http://reprap.org/wiki/G-code#M109:_Set_Extruder_Temperature_and_Wait">M109</a> (Extruder-Temperatur setzen). Das schöne daran ist, dass sehr viele 3D-Drucker und auch andere Maschinen wie Lasercutter oder CNC-Fräsen mit G-Code umgehen können. So kann die Toolchain nicht nur zum 3D-Druck verwendet werden, sondern auch zum Fräsen oder Schneiden. Welche G-Codes die Maschine unterstützt hängt dann von der Firmware ab. Ich verwende bei mir die <a href="http://reprap.org/wiki/Marlin">Marlin Firmware</a>.</p>
<p>Um den 3D-Drucker anzusteuern verwende ich meistens <a href="http://www.pronterface.com/">Pronterface aus dem Printrun Paket</a>. Die Software baut über USB eine serielle Verbindung zum 3D-Drucker auf. Hierüber können dann einzelne G-Code Befehle manuell gesendet werden oder aber ein ganze Datei (sequenziell). Hier lädt man also den zuvor generierten G-Code und startet den Druckvorgang. Bei meinem Drucker habe ich noch ein SD-Karten Interface angebaut, so dass ich den G-Code direkt auf dem Drucker ausführen kann, ohne dass ich einen PC dafür brauche.</p>
<p><img src="/images/clojure-3d-print3.png" alt="Clojure 3d Print"></p>
<p>Unter den 3D-Druckern gibt es mittlerweile sehr viele verschiedene Modelle. Ich habe meinen <a href="http://reprap.org/wiki/Prusa_Mendel">Prusa Mendel</a> vor ein paar Jahren selbst zusammengebaut. Heute kann man fertige 3D-Drucker im Baumarkt kaufen. Das Prinzip ist aber (zumindest bei den mit Extruder) gleich: Das Filament wird in Form einer langen Plastikschnur durch eine heiße Metallröhre gepresst. Am Ende ist eine Düse (Nozzle), die das heiße Plastik als Wurst auf einen Untergrund legt/drückt. Über Stepper-Motoren wird die <q>Plastik-Wurst</q> auf dem Untergrund (Bed) verteilt. Bei manchen bewegt sich der Untergrund, bei manchen der Extruder, bei manchen beides. So wird Bahn für Bahn (Ebene für Ebene) aufeinandergelegt bis so die 3D-Form entsteht.</p>
<p>Der Vorgang ist recht simpel, doch spielen sehr viele Faktoren wie aktuelle Raumtemperatur, Genauigkeit der Stepper-Motoren, Filament-Sorte, Bed beheizt oder nicht, Untergrund mit Haarspray vorbehandelt, usw. usw. mit ein. Hier muss man ein paar Stunden für die Kalibrierung aufwenden, bis alle Parameter gut aufeinander abgestimmt sind.</p>
<p><img src="/images/clojure-3d-print4.png" alt="Clojure 3d Print"></p>
<p>Die Dauer des Druckvorgangs kommt auf die Einstellungen in Slic3r und die Größe des Modells an. Eine kleine Figur braucht ca. 1 Stunde, größere Objekte können schon mal 3-4 Stunden dauern. Nachdem der Druck fertig ist, kann er vom Bed gekratzt werden (wenn es sich nicht lösen lässt, alles ein paar Minuten in den Gefrierschrank).</p>
<p>Das so gedruckte Objekt hat meist noch ein paar Schönheitsfehler, die man mit einem Skalpell gut abtrennen kann. ABS lässt sich zudem gut mit dem Dremel oder Schleifpapier nachbearbeiten, bei PLA hat man hier eher mäßigen Erfolg. ABS lässt sich auch gut mit Aceton <a href="https://www.youtube.com/watch?v=5sblh0bBHoQ">glätten</a>. PLA hingegen ist komplett ungiftig und es stinkt nicht während des Drucks. So hat jede Plastiksorte unterschiedliche Eigenschaften bzw. Vor- und Nachteile. Hier kommt es darauf an was man möchte und was man in Kauf nehmen will/kann.</p>
<p>Wer keinen 3D-Drucker besitzt, kann entweder sein nächstgelegenes FabLab/Hackerspace aufsuchen oder eine der Online-Dienste nutzen, die 3D-Printing als Dienstleistung anbieten. Selbst drucken ist natürlich viel billiger (eine Rolle ABS Filament kostet ~14 EUR und hält eine gefühlte Ewigkeit) und macht auch mehr Spaß. Zudem lernt man eine ganze Menge über Mechanik, Elektronik, Mikrocontroller und man wird sich auf jeden Fall eine digitale Schieblehre zulegen :) <a href="http://reprap.org/wiki/Mendel_Einkaufsf%C3%BChrer">Selbstbau-Kits</a> fangen mittlerweile schon bei ~400 EUR an.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/242
2016-03-15T09:30:00+01:00
2016-03-15T09:30:00+01:00
Technologie: Telegram Notifications
<p>Vor ein paar Jahren hatte so gut wie jede Seite einen RSS-Feed. Dies hat sich aber in den letzten Jahren stark verändert. Da wo früher der <q>subscribe</q> Button war, ist heute ein Facebook Like-Button, direkt gefolgt von <q>share me on twitter</q>. Sogar Radiosender haben eine WhatsApp-Gruppe für Blitzermeldungen.</p>
<p>Es ist also an der Zeit, die Notifications an die Stelle zu senden, wo sie auch gelesen werden. Alle, die <a href="https://telegram.org/">Telegram</a> nutzen, können sich jetzt in meine <a href="https://telegram.me/aaronfischer">Notification-Gruppe eintragen</a>. Hier werde ich über Neuigkeiten auf meiner Seite schreiben und das ein oder andere <q>sneak preview</q> posten.</p>
<p>Wer weiterhin ein Feed-Reader sein Eigen nennen kann, darf sich auch gerne meinen <a href="https://aaron-fischer.net/feed">Atom-Feed abonnieren</a>.</p>
<p>Vielen Dank!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/240
2016-03-10T20:00:00+01:00
2016-03-10T20:00:00+01:00
Programme und OpenSource: Buch des Monats
<p>Handerlesen und für gut befunden seit 2010 von <a href="https://twitter.com/mezzomix22">Michael Reutter</a>. Jeden Monat ein neues Buch aus seiner Sammlung zu den Themengebieten Netzkultur, Geektum, Computerspiele und Cyberpunk. Diese Bücherliste ist mittlerweile Anlaufstelle für so manchen Leser der nach neuem Lesestoff sucht. Eine kleine Webseite visualisiert seine Sammlung. Die Buchcover stammen von <a href="http://www.lovelybooks.de/">Lovely Books</a>.</p>
<p>Die Idee dazu entstand eigentlich recht spontan. Michael ist Buchhändler und Nerd, eine sehr praktische Kombination wenn man auf der Suche nach guten Buchtipps ist. Er erklärte sich bereit, <a href="https://git.okoyono.de/mezzomix/buch_des_monats/src/master/README.mkd">jeden Monat einen Buchtipp</a> zu geben. Und das macht er heute noch! Zur visuellen Unterstützung schrieb ich eine kleine <a href="https://git.okoyono.de/mezzo/buch_des_monats/src/commit/17d8fc8ea6d8a05158992799b378b3ef81e4de76">Webanwendung in Clojure</a>, die aber nach fast 10 Jahren durch eine etwas performantere Version in Go ersetzt wurde. Die Anwendung ist <a href="https://buchdesmonats.okoyono.de/">hier</a> zu finden.</p>
<p><img src="/images/buchdesmonats.png" alt="Buch des Monats"></p>
<p>Das Herzstück ist der <a href="https://git.okoyono.de/mezzo/buch_des_monats">Crawler</a>, der die Markdown-Files ausliest und alle fehlenden Buchcover herunterlädt. Für die Darstellung der Cover habe ich <a href="http://masonry.desandro.com/">Masonry</a> verwendet.</p>
<p>Wer also gerne liest, sollte sich ein Bookmark auf das <a href="https://buchdesmonats.okoyono.de/">Buch des Monats</a> setzen und ab und an einmal vorbeischauen. Oder besser noch den <a href="buchdesmonats.okoyono.de/feed.xml">RSS-Feed</a> abonnieren.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/238
2016-03-06T17:40:00+01:00
2016-03-06T17:40:00+01:00
Elektronik: emori (Funksteckdosen steuern)
<p>In meinem Arbeitszimmer standen mehrere Lampen, die aber alle schwer zugänglich waren. Diese steuerte ich mit den bekannten <a href="http://www.pollin.de/shop/dt/MzMzOTQ0OTk-/Haustechnik/Funkschaltsysteme/Funksteckdosen_Set_mit_3_Steckdosen.html">Funksteckdosen</a> von Pollin. Zudem hatte ich ein LED-Stripe am unteren Rand der Monitore geklebt, um noch etwa mehr Licht zu haben. Um alles beim Booten meines PCs anzuschalten oder bei bestimmten Ereignissen zu dimmen (Youtube Vollbild), hatte ich mir mit einem Arduino ausgeholfen.</p>
<p><img src="/images/emori.png" alt="emori"></p>
<p>Die Funksteckdosen sind recht simpel aufgebaut. Die Fernbedienung sendet per Funk auf 433/315 Mhz Steuerbefehle an die Empfänger. Das Pairing funktioniert mit DIP-Switches. Für den Arduino gibt es bereits eine <a href="https://github.com/sui77/rc-switch">Bibliothek</a>, die das Protokoll nachgebaut hat. Für den dimmbaren LED-Stripe hatte ich die normalen Arduino-Bordmittel verwendet, wie im <a href="https://github.com/f0086/emori">Code</a> zu sehen. Lediglich einen Spannungswandler musste ich dawischenschalten, da er mit 12V arbeitete.</p>
<p>Das ganze System ist relativ kompakt oberhalb vom VESA-Mount des Monitors montierbar. Für die Stromversorgung bzw. die Verbindung zum PC hatte ich den USB-Hub des Monitors verwendet. So musste ich keine weiteren Kabel zum PC legen.</p>
<p>Zur Steuerung des Systems hatte ich mir ein ganz <a href="https://github.com/f0086/emori/blob/master/emori.rb">primitives Ruby-Script</a> geschrieben, welche Befehle an den Arduino sendet. Hierüber konnte sich dann ganz einfach den LED-Stripe dimmen oder Lichter an/aus-schalten.</p>
<p>Aktuell liegt das Projekt in einer von vielen Kisten, da ich keinen Platz für den Aufbau habe. Ich wollte dennoch aufzeigen, dass man sich mit ganz simplen Mitteln eine komfortable Lichtsteuerung bauen kann (Kostenpunkt ~25 EUR für alles).</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/236
2016-02-26T22:20:00+01:00
2016-02-26T22:20:00+01:00
Elektronik: Mini LED Cube (SMD-Version)
<p>Ein 3x3x3 LED-Cube, abgeleitet von <a href="http://mosfetkiller.de/?s=miniledcube">Paul Wilhelms Projekt</a>, erweitert um USB und einigen weiteren Features. Es wurde ein AVR ATTiny2313 mit <a href="https://www.obdev.at/products/vusb/index.html">V-USB</a> (Software USB Stack) verwendet, mit dem eine Matrix aus 3x3x3 LEDs in Form eines Würfels angesteuert wird. Jede LED des Würfels kann unabhängig über ein einfaches Protokoll gesteuert werden. Mit Verschiedenen Tools (Commandline-Tool, 3D OpenGL Editor) können Animationssequenzen erstellt werden. Das Projekt ist als SMD Lötübung entstanden und als Bausatz in meinem <a href="https://www.tindie.com/products/f0086/mini-led-cube-smd/">Tindie-Shop</a> erhältlich. Version 1 kann auf <a href="http://klautesblog.blogspot.de/2011/10/chaostreff-heilbronn-ledcube.html">klautes Blog</a> angesehen werden.</p>
<p>Initial wurde das Projekt im Rahmen eines Workshops entwickelt und gelötet. Da es so gut ankam, habe ich eine SMD-Version entwickelt und einen <a href="https://www.tindie.com/products/f0086/mini-led-cube-smd/">Bausatz</a> erstellt. Jeder kann SMD-Bauteile löten, dies wollte ich mit diesem Bausatz beweisen. In mehreren <a href="http://shackspace.de/?p=4700">Workshops</a> habe ich dies unter Beweis gestellt.</p>
<p><img src="/images/ledcube2.jpg" alt="Mini LED Cube SMD"></p>
<p>Der Bausatz besteht aus zwei Teilen: Dem LED-Würfel und der Platine. Beides kann komplett unabhängig voneinander gebaut werden und eignet sich deshalb ideal für einen Workshop. So können auch zwei Personen an einem Bausatz arbeiten.</p>
<h2 id="w-rfel">Würfel</h2>
<div class="stl-viewer" data-file="/files/threedvis.stl" data-scale="1.5" width="790px" height="500px"></div>
<p>Für den Würfel wird etwas Fingerspitzengefühl benötigt. Hier kann man seine Löt-Fähigkeiten auf die Probe stellen. Die Größe spielt dabei keine Rolle. Ich habe den Würfel mit 3mm, 5mm und 10mm LEDs gelötet. Um zu sehen wie die LEDs gebogen und in welcher Richtung sie angelötet werden müssen, gibt es ein 3D SDL Modell zur Orientierung. (Für eine detailliertere Ansicht lohnt es sich, <a href="http://www.openscad.org/">OpenSCAD</a> zu installieren und die <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/doc/threedvis/threedvis.scad">threedviz.scad</a> zu laden.) Das 3D-Model ist mit Hilfe von scad-clj in Clojure <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/doc/threedvis/src/threedvis/core.clj">programmiert</a>.</p>
<iframe width="790" height="500" src="https://www.youtube.com/embed/Zgql8C2YfYU" frameborder="0" allowfullscreen></iframe>
<h2 id="die-platine">Die Platine</h2>
<p>Die Platine ist mit einfachen SMD-Bauteilen bestückt (0805), so dass das Löten mit einem normalen Lötkolben ohne spezielle Spitze möglich ist. Zum Flashen der Firmware und zum Setzen der Fuse-Bits wird natürlich ein Programmer benötigt.</p>
<p><img src="/images/ledcube1.jpg" alt="Mini LED Cube SMD"></p>
<p>Für die Montage wurde ein 3D Modell entworfen, das mit einem 3D-Drucker ausgedruckt werden kann. In das <q>Bracket</q> kann die Platine eingeklebt und mit Schrauben an einem Gehäuse oder einem anderen Untergrund befestigt werden.</p>
<div class="stl-viewer" data-file="/files/pcbracket.stl" data-scale="3.5" width="790px" height="500px"></div>
<h2 id="firmware-und-tools">Firmware und Tools</h2>
<p>Die Firmware stammt von <a href="http://klautesblog.blogspot.de">klaute</a>. Er hat das unmögliche möglich gemacht und in einen <a href="http://www.atmel.com/images/doc2543.pdf">ATiny2313</a> mit 2KB Flash-Speicher den kompletten USB-Stack, die Steuerung der 27 LEDs, und ein Kommunikations-Protokoll gequetscht. Zudem können Frames und ganze Animations-Sequenzen in den 128 Bytes des EEPROM gespeichert und beim Booten geladen werden. Zum Betrieb des LED-Cubes entstanden ebenfalls mehrere Tools.</p>
<ul>
<li>Die <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/client">Client-Bibliothek</a> ist ein simples Interface für weitere Software-Implementierungen. Zum Einstieg lohnt es sich die <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/client/demo.c">demo.c</a> Datei anzusehen.</li>
<li>Mit dem Commandline-Client <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/client/clcc.c">clcc</a> kann der Cube über die Console bedient werden. Es gibt verschiedene Befehle um den Cube zu steuern. Ideal für die Automatisierung.</li>
<li>Der <a href="https://git.okoyono.de/aaron/mini-led-cube/src/master/editor">3D OpenGL Editor</a> benutzt die oben genannte Client-Bibliothek. In einer grafischen Anzeige können Animationen für den Cube erstellt und direkt auf den Cube übertragen werden -- ganz simpel mit der Maus.</li>
</ul>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/235
2016-02-21T12:49:00+01:00
2016-02-21T12:49:00+01:00
Elektronik: MX-Board
<p>Ein 3 Tasten Breakout-Board für Cherry MX Schalter. Hiermit lassen sich hochwertige mechanische Tastatur-Schalter an einen Arduino oder RaspberryPi anschließen. Erhältlich zum Selbstkostenpreis in meinem <a href="https://www.tindie.com/products/f0086/mx-board/">Tindie Shop</a> -- natürlich alles 100% Handarbeit.</p>
<p><img src="/images/mxboard1.jpg" alt="MX Board"></p>
<p>Das Board lässt sich einfach über die Kabel an die I/O-Pins eines Arduino oder RaspberryPi anschließen. Jeder Taster hat einen Pull-Up Widerstand, um den <q>echten</q> Zustand auszulesen (gedrückt bedeutet HIGH, nicht gedrückt bedeutet LOW). Zudem hat jeder Taster eine Status-LED, die ebenfalls einzeln über Kabel angesteuert werden kann. Mit den LEDs kann beispielsweise ein Schalter-Zustand angezeigt werden (an/aus).</p>
<p>Die Taster sind original mechanische <strong>Cherry MX Black</strong>, made in Germany. Dies sind eine der besten mechanischen Taster für Tastaturen die man kriegen kann! Sie sind linear und haben einen <q>non-clicky</q> Auslösepunkt (60 cN Auslösekraft). Die Kappen sind schwarze <strong>DSA</strong> (ABS Plastik) von der Firma Signature Plastic, einer der führenden Tastaturkappen-Hersteller. Die Kappen können natürlich gegen beliebige andere durch abstecken ausgetauscht werden. Es wurden hier nur sehr hochwertige Komponenten ausgewählt und verbaut.</p>
<h2 id="anschluss">Anschluss</h2>
<p>Die Verdrahtung mit einem Arduino ist sehr einfach. VCC und GND mit Strom versorgen. S1-S3 mit jeweils einem I/O-Pin am Arduino verbinden und diesen als Input definieren. Dies genügt schon, um die Taster abzufragen.</p>
<p><img src="/images/mxboard2.png" alt="MX Board"></p>
<p>Hier ein einfacher Beispiel-Sketch:</p>
<pre><code>const int buttonPin = 10;
const int ledPin = 11;
void setup() {
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
}
void loop() {
// The switches are equiped with pull-down resistors. So if the
// button is pressed, you read a HIGH, if the button is released
// you read a LOW.
digitalWrite(ledPin, digitalRead(buttonPin));
}
</code></pre>
<p>Das Board selbst kann auch auf <a href="https://oshpark.com/shared_projects/PogDjaIS">OSH-Park</a> bestellt werden. Wer sich gar die Platine selbst machen möchte, kann <a href="https://git.okoyono.de/aaron/mx-board">hier</a> alle nötigen Files dazu finden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/237
2016-02-15T20:50:00+01:00
2016-02-15T20:50:00+01:00
Programmiersprachen: Destructing in Clojure
<p><a href="http://clojure.org/reference/special_forms#binding-forms">Destructing</a> (auch Abstract Structural Binding genannt) ist eines von vielen Features in <a href="http://clojure.org">Clojure</a>, die die Sprache für mich so unglaublich elegant und schön machen. Wer noch nie mit Clojure programmiert hat, sollte jetzt genau aufpassen :) <em>(Achtung: Ich rede hier von Variablen, doch korrekterweise sind es lexikalische Bindungen. Alle Bindungen in Clojure sind unveränderbar (immutable), also keine Variablen. Dennoch ist es für das Verständnis einfacher sie Variablen zu nennen.)</em></p>
<p>In der <a href="http://clojure.org/reference/special_forms#binding-forms">Doku</a> steht ganz unspektakulär:</p>
<blockquote>
<p>Clojure supports abstract structural binding, often called destructuring, in let binding lists, fn parameter lists, and any macro that expands into a let or fn. The basic idea is that a binding-form can be a data structure literal containing symbols that get bound to the respective parts of the init-expr. The binding is abstract in that a vector literal can bind to anything that is sequential, while a map literal can bind to anything that is associative.</p>
</blockquote>
<p>Es geht also darum, <q>Variablen</q>-Zuweisungen in Form von Listen zu machen. PHP bietet dafür eine ganz primitive Version davon an:</p>
<pre><code class="php">list($firstname, $lastname, $rest) = ['Aaron', 'Fischer', 'other', 'stuff'];
</code></pre>
<p>Dies wird oft <q>Multiple Variable Assignment</q> genannt. In Ruby gibt es zusätzlich den splat-Operator, der ähnliches kann. </p>
<pre><code class="ruby">def (firstname, lastname, *rest)
...
end
</code></pre>
<p>Das ist natürlich nur die Spitze des Eisbergs. In Clojure wurde diese Funktionalität komplett abstrakt umgesetzt und funktioniert mit jeder Art von Listen. Man muss sich das wie <a href="https://en.wikipedia.org/wiki/Pattern_matching">Pattern Matching</a> vorstellen. Es ist unglaublich, wie mächtig und nützlich Destructing ist. Besser erklären lässt es sich anhand ein paar Beispielen:</p>
<p>Hier wird wie oben aus einem Vektor der Vorname, der Nachname und der Rest (other stuff) herausgezogen und mit <code>let</code> an die drei <q>Variablen</q> gebunden.</p>
<pre><code class="clojure">(let [[firstname lastname & rest] ["Aaron" "Fischer" "other" "stuff"]]
...)
</code></pre>
<p>Das ganze lässt sich auch bei Funktionen in den Parametern verwenden:</p>
<pre><code class="clojure">(defn add-vectors [[a1 a2] [b1 b2]]
[(+ a1 b1) (+ a2 b2)])
(add-vectors [1 2] [5 6])
</code></pre>
<p>Das macht den Code viel lesbarer und verständlicher. In anderen Programmiersprachen hat man bei solcher Art von Funktionen oft zu Beginn eine Liste mit Variablenzuweisungen und viel Array-Fummelei.</p>
<p>Das schöne daran ist, es lässt sich beliebig verschachteln und funktioniert mit jeder Form von Listen -- also allem was irgend wie mit <a href="http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/nthnext">nthnext</a> bedient werden kann. Hier ein weiteres Beispiel (<code>{:keys [a b]}</code> ist ein Shortcut für <code>{a :a b :b}</code>):</p>
<pre><code class="clojure">(defn foo [{name :full-name, [x y _] :coordinates, {:keys [n e]} :direction}]
...)
(foo {:full-name "Aaron Fischer"
:coordinates [24 32 123]
:other "stuff"
:direction {:n 0, :e 60}})
</code></pre>
<p>So kann man beliebig komplexe Datenstrukturen ganz einfach zerlegen (= destruct), in dem man ein Muster definiert, das auf die Datenstruktur passt. So lassen sich alle relevanten Informationen schon im <code>let</code> bzw. <code>fn</code> extrahieren und danach direkt damit arbeiten.</p>
<p>Destructing wird auch ausgiebig von Bibliotheken verwendet, die mit großen verschachtelten Datenstrukturen umgehen müssen. <a href="https://github.com/weavejester/compojure">Compojure</a> zum Beispiel nutzt es, um durch den DOM-Baum zu navigieren und relevante Informationen herauszupicken/auszutauschen.</p>
<p>Hat man sich an dieses Luxus-Feature erst einmal gewöhnt, sucht man es schmerzlich bei anderen Programmiersprachen (<a href="https://doc.rust-lang.org/book/patterns.html#destructuring">Rust</a> hat ebenfalls Destructing, in <a href="https://en.wikibooks.org/wiki/Haskell/Pattern_matching">Haskell</a> ist es zentraler Bestandteil der Sprache). Die hier gezeigten Beispiele umspannen allerdings noch nicht den vollen Umfang von Destructing. Es gibt noch einige Features wie Default-Werte, eine Art erweiterter splat-Mechanismus, usw. Es lohnt sich also, mal in der eigenen Lieblingssprache nach diesem Feature zu suchen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/232
2016-02-12T20:30:00+01:00
2016-02-12T20:30:00+01:00
Game Jams: Clojure Cup 2014 (Luduverse)
<p>Die Bildergalerie auf <a href="http://ludumdare.com/compo/">Ludum Dare</a> ist extrem groß und unübersichtlich. Mit einer besseren Darstellung und einer guten Suchfunktion wären die Spiele viel zugänglicher und einfacher zu finden. Dieses Problem wollte ich beim <a href="http://2014.clojurecup.com/#/">Clojure Cup 2014</a> lösen. Leider reichte die Zeitvorgabe von 48 Stunden nicht aus, um ein stabiles Ergebnis zu liefern. Dennoch ist durch dieses Projekt eine nützliche Bibliothek entstanden. Sollte ich Zeit finden das Projekt abzuschließen, habe ich bereits alle Daten.</p>
<p>Das Projekte bestand aus drei Teilen: Der Scraper, der den Content von der Ludum Dare Webseite herunterlädt und aufbereitet, die Such-Engine mit entsprechendem Index und das Web-UI, über das die Daten zugänglich gemacht werden.</p>
<p><img src="/images/luduverse1.png" alt="Luduverse"></p>
<h2 id="scraping">Scraping</h2>
<p>Die Ludum Dare Webseite ist ein Wordpress mit angepasstem Theme und Bildergallerie-Plugin. Der HTML-Code ist extrem chaotisch, doch es gibt ein gewisses Muster, über das die entsprechenden Attribute gefunden werden können. Der Scraper arbeitet in mehreren Schritten: Zuerst werden die einzelnen Gallerie-Seiten geladen, von dort aus werden die einzelnen Profil-Seiten der Spiele geladen. Diese Links werden dann in eine Datenbank geschrieben und parallel abgearbeitet. Jede Profilseite wird geladen, die relevanten Informationen entnommen (Titel, Autor usw.), sowie die Links zu den Bildern gespeichert. Die Bild-Links werden separat heruntergeladen und alle auf eine einheitliche Größe gebracht und in drei unterschiedlichen Größen gespeichert (quadratisches Thumbnail, kleines Bild und großes Bild). Alle gewonnen Informationen werden in einer Datenbank gespeichert. Der Scraping-Prozess kann jederzeit unterbrochen und wiederaufgenommen werden. So konnte ich die Datengewinnung rund um die Uhr laufen lassen und bei Gelegenheit unterbrechen.</p>
<h2 id="such-engine">Such-Engine</h2>
<p>Für die Suche habe ich <a href="http://clojureelasticsearch.info/">Elastisch</a> verwendet (eine Bibliothek für Elastic Search). Hierbei wird ein Index angelegt, der den kompletten Inhalt der Datenbank enthält. Über die Query-DSL können dann Suchanfragen gestartet werden. Die Clojure Bibliothek macht dies extrem einfach, so dass dieser Teil relativ schnell erledigt war. Natürlich lässt sich in den Such-Index noch mehr Energie stecken (Stemming, mehrere Sprachen, Synonyme, ...), doch für meine Zwecke genügte so.</p>
<h2 id="web-ui">Web-UI</h2>
<p>Die Weboberfläche kam leider etwas zu kurz, deshalb ist dieses Projekt auch nicht komplett fertig geworden. Ich habe begonnen mit der genialen Bibliothek <a href="https://github.com/cgrand/enlive">Enlive</a> und <a href="https://github.com/weavejester/compojure">Compojure</a> das Frontend zu bauen. Clojure ist mit <a href="https://github.com/ring-clojure/ring">Ring</a> extrem flexibel, aber auch extrem vielfältig. Es gibt nicht den einen Weg wie man es aus anderen Programmiersprachen kennt (Ruby/Rails, PHP/Symfony, Python/Django). Es gibt auch kein Framework in diesem Sinne. Man ist dazu angehalten, die für das Projekt benötigten Komponenten nach Belieben zusammenzustecken. Es gibt zwar <a href="http://www.luminusweb.net/">Luminus</a>, doch dies ist nur eine Ansammlung an Bibliotheken und einer <q>Getting started</q>-Dokumentation. Zudem möchte man in Clojure-World seine Komponenten selbst zusammenstellen. Deshalb macht es die Auswahl recht schwierig, da es so viele verschiedene Bibliotheken gibt. </p>
<p>Wenn ich Zeit finde, werde ich dieses Projekt noch zuende führen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/234
2016-02-10T17:52:00+01:00
2016-02-10T17:52:00+01:00
Technologie: Die 1€ Gaming-Maus
<p>Auf eBay gibt es zur Zeit eine Gaming-Maus für 1 EUR inklusive Versand (DHL) zu <a href="http://www.ebay.de/itm/141715155139">kaufen</a>. Das Päckchen war nach zwei Tagen da und es war wirklich die beschriebene Maus darin. Doch wie ist das möglich? Lässt sich eine Gaming-Maus so billig produzieren?</p>
<p><img src="/images/1eurmaus1.png" alt="China mouse"></p>
<p>Der Verkäufer nennt sich Penossi. Als Artikelstandort ist Hamburg angegeben, in seinem Profil steht <a href="https://www.youtube.com/watch?v=EBtRJc-z05k">Shenzhen</a>. Verschickt wurde tatsächlich aus Hamburg. Vermutlich versucht hier ein chinesischer Händler in Deutschland Fuß zu fassen und sammelt positive Bewertungen, um dann später die Maus durch ein anderes Produkt zu ersetzen. So hat das teure Produkt viele positive Bewertungen und das begehrte <q>schon x mal verkauft</q> Label.</p>
<h2 id="identifikation">Identifikation</h2>
<p>Die Maus selbst funktioniert direkt nach dem einstecken. Der Kernel lädt den default HID-Treiber. <code>xinput list</code> benennt das Gerät als <strong>USB OPTICAL MOUSE</strong>.</p>
<pre><code>[ 67.678603] hidraw: raw HID events driver (C) Jiri Kosina
[ 67.696340] usbcore: registered new interface driver usbhid
[ 67.696344] usbhid: USB HID core driver
[ 67.700156] input: USB OPTICAL MOUSE as /devices/pci0000:00/0000:00:1d.0/usb4/4-2/4-2:1.0/0003:0000:0538.0001/input/input15
[ 67.700489] hid-generic 0003:0000:0538.0001: input,hidraw0: USB HID v1.11 Mouse [ USB OPTICAL MOUSE] on usb-0000:00:1d.0-2/input0
</code></pre>
<p>Die Vendor-ID ist <strong>0x0000</strong>, was extrem merkwürdig ist. Die Product-ID ist <strong>0x0538</strong>. Leider lässt sich dazu absolut nichts finden.</p>
<pre><code>idVendor 0x0000
idProduct 0x0538
</code></pre>
<p>Auf der Schachtel der Maus ist das Logo der Firma <a href="http://www.floureon.com/">FLOUREON</a> zu finden. Dies ist eine Firma aus USA die hauptsächlich Batterien herstellt. Eine Maus haben sie nicht im Sortiment -- zumindest nicht auf der Webseite. Vermutlich wurden hier übrig gebliebene Schachteln wiederverwendet. Auf der Maus selbst ist ebenfalls ein Logo, welches ich aber ebenfalls nicht zuordnen kann. Sieht nach einem Fake-Logo aus. Die Maus und auch die Form, die Farben und das Design erinnern stark an <a href="http://www.roccat.org/de-DE/Home/Overview/">Roccat</a>.</p>
<p><strong>Nachtrag 13.02.2016:</strong> Christian Petersen hat das Logo der Maus erkannt! Es befindet sich auf einer anderen Maus, die wiederum eine richtige Modellnummer enthielt die ihn zu <a href="http://www.szkodz.com/">Szkodz</a> führte. Leider wird sie nicht im Sortiment geführt, doch sieht <a href="http://www.szkodz.com/page391?product_id=24">diese hier</a> der 1 EUR Maus sehr ähnlich. Vielen Dank für diesen Hinweis!</p>
<p><strong>Nachtrag 05.02.2017:</strong> Jonas Roth hat eine sehr ähnliche Maus auf <a href="http://www.ebay.de/itm/Rajfoo-I5-1600-DPI-3-Button-Optical-USB-Wired-Gaming-Mouse-Mice-For-PC-Laptop-/252251448173?hash=item3abb5ba36d:g:fAsAAOSwoydWmGco">eBay</a> entdeckt, die allerdings ein anderes Logo enthält, welches mit <q>Rajfoo</q> betitelt wird. Es ähnelt allerdings auch hier wieder einem bekannten Gaming-Herstelle; dieses Mal <a href="http://www.rapoo.cn/">Rapoo</a>. Es scheint also wieder eine billig-Kopie zu sein, die mit dem Branding von Rapoo mitschwimmen will. Danke für den Hinweis, Jonas! Wer mehr Details zu dieser Maus hat, kann sich gerne bei mir Melden, ich bin gespannt ob sich an der Hardware etwas verändert hat.</p>
<h2 id="features">Features</h2>
<p><code>xinput</code> kann leider nicht viel herauslesen. Hier die Ausgabe von <code>xinput list-props</code>:</p>
<pre><code>Device ' USB OPTICAL MOUSE':
Device Enabled (136): 1
Coordinate Transformation Matrix (138): 1.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 1.000000
Device Accel Profile (260): 0
Device Accel Constant Deceleration (261): 1.000000
Device Accel Adaptive Deceleration (262): 1.000000
Device Accel Velocity Scaling (263): 10.000000
Device Product ID (256): 0, 1336
Device Node (257): "/dev/input/event14"
Evdev Axis Inversion (264): 0, 0
Evdev Axes Swap (266): 0
Axis Labels (267): "Rel X" (146), "Rel Y" (147), "Rel Vert Wheel" (418)
Button Labels (268): "Button Left" (139), "Button Middle" (140), "Button Right" (141), "Button Wheel Up" (142), "Button Wheel Down" (143), "Button Horiz Wheel Left" (144), "Button Horiz Wheel Right" (145)
Evdev Scrolling Distance (269): 1, 1, 1
Evdev Middle Button Emulation (270): 0
Evdev Middle Button Timeout (271): 50
Evdev Third Button Emulation (272): 0
Evdev Third Button Emulation Timeout (273): 1000
Evdev Third Button Emulation Button (274): 3
Evdev Third Button Emulation Threshold (275): 20
Evdev Wheel Emulation (276): 0
Evdev Wheel Emulation Axes (277): 0, 0, 4, 5
Evdev Wheel Emulation Inertia (278): 10
Evdev Wheel Emulation Timeout (279): 200
Evdev Wheel Emulation Button (280): 4
Evdev Drag Lock Buttons (281): 0
</code></pre>
<p>Die Maus hat also drei Tasten und ein Mausrad, mehr nicht. Was <code>Button Horiz Wheel Left</code> und <code>Button Horiz Wheel Right</code> sein soll habe ich nicht herausgefunden. </p>
<p><strong>Nachtrag 27.04.2016:</strong> Sebastian hat mich darauf hingewiesen, dass die beiden Scroll-Features wohl das horizontale Scrollen mit dem Mausrad sein sollen. Manche Mäuse unterstützen dies -- ähnlich wie die Apple Magic-Mouse.</p>
<p>Konfigurieren lässt sich die Maus wie gewohnt mit <code>xset</code>. Da die Maus recht schnell ist, lohnt sich folgende Einstellung:</p>
<pre><code>xset m 1/1 2
</code></pre>
<p>Ansich ist die Maus benutzbar, aber mehr auch nicht. Sie ist nicht wirklich akkurat und ohne Mousepad funktioniert sie garnicht. Die Tasten sind angenehm zu klicken und das Mausrad funktioniert auch so wie es soll, doch würde vermutlich jeder Gamer die Maus nach zwei Minuten in den Müll werfen.</p>
<h1 id="das-innere">Das Innere</h1>
<p>Um ein Indiz auf den Hersteller zu finden und die Frage <q>Warum ist die Maus so billig?</q> zu klären, müssen wir hineinsehen. Geöffnet werden kann die Maus gewaltfrei mit zwei Schrauben unter den Slide-Pads im unteren Bereich.</p>
<p><img src="/images/1eurmaus3.png" alt="China mouse"></p>
<p>Hier wird einiges klar. Das angekündigte <q>Internal Gravity Configuration System</q> ist nur ein Stück Metall/Blei, das mit einer Schraube befestigt ist. Das soll wohl die Maus etwas schwerer machen. Die Platine selbst hat erstaunlich wenig Komponenten. Als PCB-Material wurde Hartpapier verwendet, das wohl billigste Material zur Platinenherstellung. Die Bezeichnung <strong>MS-0910</strong> ergibt auch keine wirklichen Aufschlüsse.</p>
<p><img src="/images/1eurmaus4.png" alt="China mouse"></p>
<p>Die Zentrale Komponente ist die optische Einheit, die irgend wie alles zu machen scheint. Als Bezeichnung steht <strong>TYJ-3511D</strong> und darunter <strong>15J10</strong> auf dem Gehäuse. Leider ist auch dazu nichts zu finden. Der Hersteller scheint wirklich sorge dafür zu tragen, nicht entdeckt zu werden. Wer etwas mit der Part-Number anfangen kann, darf sich gerne bei mir melden.</p>
<p><strong>Nachtrag 10.06.2016:</strong> Es existieren offensichtlich mehrere Versionen der Platine. Asumag hat auf seiner Platine <strong>15H20</strong> stehen. Danke für den Hinweis.</p>
<p><img src="/images/1eurmaus5.png" alt="China mouse"></p>
<p>Die nicht bestückten Komponenten auf der Platine sind lediglich LEDs und deren Widerstände die direkt auf VCC gehen. Es gibt wohl noch eine Luxusvariante mit Beleuchtung. Um den Chip besser zu verstehen, habe ich ein Schaltbild gezeichnet:</p>
<p><img src="/images/1eurmaus2.png" alt="China mouse"></p>
<p>Es sind wirklich nur die minimal notwendigen Komponenten verwendet worden. Der Chip scheint speziell für diese Art von Maus konzipiert worden zu sein. Drei Buttons, ein Potentiometer für das Scrollrad, die LED für die Optik und der USB-Anschluss.</p>
<p>Die Materialkosten sind vermutlich extrem gering. Die Platine und das Plastikgehäuse kosten bei entsprechenden Stückzahlen nur ein paar Cents. Die Taster und die restlichen Komponenten ebenfalls 5-10 Cent und der Chip vermutlich ~10 Cent. Zusammen mit dem Stück Metall und dem Kabel liegt der Herstellungspreis vermutlich bei ~20-30 Cent. Jetzt noch einen guten Deal mit DHL für einen günstigen Versand aushandeln und der Händler macht nicht mal Verlust.</p>
<p><strong>Nachtrag 26.04.2016:</strong> Dieser Artikel ist Grundlage für einen <a href="http://www.pcgameshardware.de/Maeuse-Hardware-255539/News/1-Euro-Maus-auf-Ebay-Was-taugt-der-Gaming-Nager-1193527/">Artikel auf PC Games Hardware</a>.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/233
2016-02-09T15:00:00+01:00
2016-02-09T15:00:00+01:00
Elektronik: Nagios Light (XFD #1)
<p>Um sehr viele Server und Services rund um die Uhr zu überwachen, gibt es verschiedene Tools. Eines davon ist <a href="https://www.nagios.org/">Nagios</a>. Gibt es ein Problem, wird ein Alarm ausgelöst und die dafür eingestellten Verantwortlichen werden per E-Mail benachrichtigt. Will man aber schneller reagieren, braucht man ein eXtreme Feedback Device. Es meldet sich selbst, wenn ein Problem auftritt. Ursprünglich als Proof of Concept gedacht, übernimmt das Gerät nach knapp zwei Jahren Dauereinsatz viele weitere Aufgaben und wurde zum kleinen Helfer im Büro.</p>
<h2 id="aufbau">Aufbau</h2>
<p>Der Turm wurde mit einer Alu-Konstruktion gebaut. Winkel- und Hohlprofile wurden mit Schrauben und Nieten zu einem stabilen Unterbau für die Lampe und die Elektronik verbunden.</p>
<p><img src="/images/nagioslight1.png" alt="Nagios Light"></p>
<p>An der Spitze befindet sich eine industrielle Signallampe, die man aus Supermärkten oder von IKEA-Kassen kennt. Diese sind recht günstig gebraucht bei eBay oder aus Firmenauflösungen zu bekommen. Darin befinden sich vier 24V LED-Lampen, die einzeln geschaltet werden können.</p>
<p>Auch ganz oben am Turm ist ein <a href="https://www.adafruit.com/products/1002">4x 7-Segment Display</a> von Adafruit montiert. Mit einem <a href="https://twitter.com/fu86/status/535208722730340354">kleinen Halter</a> aus PLA Plastik wurde es direkt an der Lampe befestigt.</p>
<p><img src="/images/nagioslight2.png" alt="Nagios Light"></p>
<p>Darunter ist <a href="http://www.sainsmart.com/arduino-compatibles-1/relay/8-channel-dc-5v-relay-module-for-arduino-pic-arm-dsp-avr-msp430-ttl-logic-1.html">eine Relais-Platine</a> mit acht steuerbaren Relais montiert. Mit diesen lassen sich beliebige 230V-Geräte schalten. So auch die einzelnen Lampen, da sie eine andere Versorgungsspannung (24V) benötigen. Die Platine hat einen 5V Schaltpegel.</p>
<p>Um die verschiedenen Spannungen über eine einzige Stromversorgnung zu betreiben und die Pegelwandlung von 3.3V auf 5V vorzunehmen, wurde <a href="https://bitbucket.org/fu86/xfd-tower/src">eine kleine Platine</a> entwickelt. Als Stromversorgnung dient ein altes 12V Notebook-Netzteil. Zur Erzeugung der erforderlichen 24V wurde ein <a href="http://de.aliexpress.com/item/1-Pcs-DC-DC-LM2596-Step-Down-Adjustable-Converter-Power-Supply-Module-Newest/1814236129.html">DC-DC Power Converter</a> verwendet. Die Platine versorgt alle Komponenten mit der benötigten Spannung, nimmt die Pegelwandlung vor und verbindet sie untereinander. Da der Spannungsregler relativ heiß wird, wurde er mit einem Kühlkörper am Turm befestigt.</p>
<p><img src="/images/nagioslight3.png" alt="Nagios Light"></p>
<p>Als zentrale Steuereinheit dient der allseits bekannte <a href="https://www.raspberrypi.org/products/raspberry-pi-2-model-b/">Raspberry Pi</a>. Dieser steuert über ein paar Scripts die Relais-Platine und damit die Lampen. Über angeschlossene Aktiv-Boxen wird es als Musikabspielgerät verwendet. Das 7-Segment Display zeigt nützliche Informationen an. Bei einem Serverausfall bspw. die Servernummer. Oder aber die aktuell offenen Tickets. Als Betriebssystem kommt ein vorgefertigtes Image für den RaspberryPi zum Einsatz. Die Scripts sind in Bash und Ruby geschrieben (Abfrage des Nagios-Status, Ticketsystem usw.).</p>
<p><img src="/images/nagioslight4.png" alt="Nagios Light"></p>
<h2 id="ausblick">Ausblick</h2>
<p>Ursprünglich war es als kleiner Test geplant, um schneller bei einem Serverausfall zu reagieren. Doch der Nagios-Turm hat sich etabliert und ist nicht mehr wegzudenken. Er steht nun seit fast zwei Jahren im Büro und läuft rund um die Uhr. Es ist extrem hilfreich, wenn man den Komplettzustand des System auf einen Blick sehen kann. </p>
<p>Es sind noch viele Erweiterungen denkbar, die vermutlich irgend wann folgen werden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/224
2016-02-05T22:01:00+01:00
2016-02-05T22:01:00+01:00
Websites: nitrado.net Gameserver Webinterface
<p>Für die Firma <a href="http://nitrado.net/">marbis GmbH</a> entstand in Zusammenarbeit mit meinen Arbeitskollegen <a href="https://tyrola.at">Alex</a> das neue Gameserver Webinterface zur Administration von gemieteten Dienstleistungen. Besonderes Augenmerk wurde hier auf die Ausfallsicherheit der Web-Oberfläche und der darunterliegenden Dienste gelegt. Der Kunde bezahlt die Leistungen im Vorraus und hat dementsprechend Anspruch auf die gemieteten Dienste. Mit dem Nitrado Gameserver Webinterface kann der Server per Browser administriert und mit ihm interagiert werden. Mittlerweile sind über 50 verschiedene Spiele integriert.</p>
<p>Eine Neuimplementierung des in die Jahre gekommenen Gameserver Webinterface wurde unumgänglich. So flossen die in den vergangenen Jahren gelernten Erfahrungen mit diesem in die Konzeption und Umsetzung eines neuen Systems.</p>
<p><img src="/images/nitrado_webinterface_alt.png" alt="Altes Webinterface"></p>
<p>Die Programmiersprache und die Datenbank wurden beibehalten (PHP, MySQL), doch als Basis wird nun das Framework <a href="http://symfony.com/">Symfony</a> verwendet. Ziel war es, eine flexible und modulare <q>Toolbox</q> zu schaffen, auf der dann Spiel für Spiel auf das neue System migriert werden kann. Am Ende sollte es möglich sein, neue Spiele innerhalb von ein paar Stunden in das neue System einzubinden. Bestandsdaten, sowie sämtliche Features des alten Systems mussten migriert und nachgebaut werden, so dass für den Kunden keine Verschlechterung der Dienstleistung eintritt.</p>
<p><img src="/images/nitrado_webinterface.png" alt="Webinterface"></p>
<p>Features, die jedes Spiel gemeinsam haben wie einen Dateibrowser oder der Logfile-Viewer wurden so umgesetzt, dass die nur ein Mal implementiert werden mussten. Speziell für jedes Spiel sind die Einstellungen: Diese werden auf den unterschiedlichsten Wegen am Gameserver vorgenommen. So wurde eine Abstraktion von UI (Schieberegler, Eingabefelder, Beschreibungen, Fehlermeldungen, Constraints, ...) zu der eigentlichen Konfiguration am Gameserver geschaffen. Diese erlaubt es nun, sehr einfach neue Einstellungen hinzuzufügen.</p>
<p><img src="/images/nitrado_webinterface_settings.png" alt="Settings"></p>
<p>Einige Spiele benötigen darüber hinaus spezielle Views und Funktionen, die nun problemlos implementiert werden können, ohne den Rest des Programmcodes anzupassen. Bspw. die <a href="https://minecraft.net/">Minecraft</a> <a href="https://overviewer.org/">Overview-Map</a>, die Auswahl von Karten/Mods für <a href="https://dayzmod.com/">DayZ</a> oder die <a href="https://developer.valvesoftware.com/wiki/Source_RCON_Protocol">RCON</a> Echtzeit-Serverconsole.</p>
<p>Da das Webinterface in verschiedenen Ländern genutzt wird, musste es in 9 Sprachen übersetzt werden. Hierfür wurde ein Automatismus entwickelt, mit dem neue Übersetzungen direkt an die entsprechenden Übersetzer delegiert und nach Beendigung wieder zurück in die Codebase eingepflegt werden können. Nicht nur in verschiedenen Sprachen, sondern auch auf den verschiedensten Geräten (Vom Smartphone bis zum TripleHead Gaming-PC) wird das Webinterface genutzt. So muss sich das UI komplett responsive verhalten.</p>
<p>Gameserver haben besondere Anforderungen an die Hardware und das Netzwerk. Die Latenz beeinflusst den Spielfluss. Profi-Spieler merken auch kleinste Veränderungen. Bricht die Verbindung nur kurzzeitig ab, werden (bei einigen Spielen) alle Spieler aus dem Spiel geworfen. Dieser Umstand macht Gameserver ideale Ziele für DDoS-Angriffe. Aus diesem Grund wird mit teurer Spezialhardware der Traffic live überwacht und Auffälligkeiten mitigiert. Um dies dem Kunden zu visualisieren, werden dynamisch Graphen aus den Traffic-Daten erzeugt.</p>
<p><img src="/images/nitrado_ddos.png" alt="DDoS Analyse und Mitigation"></p>
<p>Für Mitarbeiter im Support ist das Webinterface ein Arbeitswerkzeug, um dem Kunden bei Problemen zu assistieren. Hierfür wurden spezielle Funktionen eingebaut, die nur Support-Mitarbeitern zur Verfügung stehen.</p>
<p>Als Backend wurde anfangs das alte System verwendet und Funktion für Funktion durch das <a href="http://nitrado.github.io/Nitrapi/">NitrAPI</a>-Projekt ersetzt. Das neue Nitrado Webinterface wird laufend weiterentwickelt und optimiert.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/231
2016-01-29T15:14:00+01:00
2016-01-29T15:14:00+01:00
Game Jams: Clojure Cup 2015 (Mailhead)
<p>Der <a href="http://clojurecup.com/">Clojure Cup</a> findet seit drei Jahren im Dezember statt. Die Aufgabe ist, in Clojure eine Webanwendung zu entwickeln. Ein Thema gibt es nicht. Die Zeitvorgabe sind 48 Stunden. Es wird eine VM bereitgestellt, auf der die Webanwendung deployed werden muss. Auch dieses Jahr habe ich wieder mitgemacht und ein Tool zur Analyse von E-Mail Headern entwickelt. Ich habe dabei den Platz 10 belegt.</p>
<p>Das Tool hilft bei der Analyse von E-Mail Headern. Es zeigt auf, welchen Weg die E-Mail vom Sender zum Empfänger geht und wo die Engpässe sind. Zudem werden interessante Informationen wie verwendete Software oder wie lange die E-Mail insgesamt unterwegs war dargestellt. Der Benutzer kopiert den E-Mail Header in die Textbox und klickt auf analysieren.</p>
<p><img src="/images/mailhead1.png" alt="Mailhead"></p>
<p>Das Ergebnis der Analyse ist ein Graph, der den Verlauf der E-Mail aufzeigt. Darunter sind die nützlichen Informationen zusammengefasst.</p>
<p><img src="/images/mailhead2.png" alt="Mailhead"></p>
<p>Durch die Visualisierung werden interne Strukturen, Probleme und unnötige Wege sichtbar, die man so aus dem Header nicht direkt herauslesen konnte.</p>
<p><img src="/images/mailhead3.png" alt="Mailhead"></p>
<p>Der E-Mail Header ist im <a href="https://www.w3.org/Protocols/rfc822/">RFC822</a> definiert, leider hält sich daran so gut wie kein MUA bzw. MTA. Speziell die Formatierung des Zeitstempels variiert extrem stark. Leider gibt es bis heute keinen E-Mail Header Parser, der zuverlässig funktioniert. Für dieses Projekt habe ich die bekannteste Bibliothek verwendet und diese etwas robuster gemacht. Leider funktionieren immer noch viele Header nicht korrekt.</p>
<p>Ich hatte natürlich noch viele weitere Ideen, dafür war aber keine Zeit mehr:</p>
<ul>
<li>Eine Flagge an jede Box, so könnte man direkt sehen, welche Länder die E-Mail auf ihrer Reise durchquert. Das Land lässt sich über die IP ermitteln.</li>
<li>Den Host anpingen um an die IP zu gelangen. Mit dieser dann den <a href="https://www.arin.net/resources/whoisrws/whois_api.html">ARIN REST Service</a> nutzen, um den Firmennamen auszulesen.</li>
<li>Den MTA auslesen (wenn gegeben) und in jeder Box anzeigen.</li>
<li>Zusatz-Marker für durchgeführte Anti-Virus-Checks oder Spam-Checks am MTA</li>
</ul>
<p>Der Contest-Server wurde schon abgeschaltet, deshalb gibt es keine Live-Demo. Der Code ist aber <a href="https://github.com/f0086/clojurecup2015-mailhead">auf Github</a> zu finden und kann mit <code>lein run</code> ausgeführt werden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/230
2016-01-25T23:22:00+01:00
2016-01-25T23:22:00+01:00
Websites: aaron-fischer.net
<p>Seit mehr als einem Jahrzehnt betreibe ich unter aaron-mueller.de und aaron-fischer.net meine private Website. Diese durchlief in den vergangenen Jahren mehrer Überarbeitungen und Neuimplementierungen. In einem kurzen Abriss werden die vergangenen Websites präsentiert und im Anschluss die Technik und Motivation die hinter der aktuellen Webseite steckt vorgestellt.</p>
<h1 id="geschichte">Geschichte</h1>
<p>Es ist mir ein Anliegen, meine Webseite selbst zu programmieren. So kann ich einfach neue Technologien ausprobieren und Features implementieren, die ich bei anderen fertigen Systemen vermisse. Dennoch habe ich anfangs die Blog-Engine <a href="http://sourceforge.net/projects/sphpblog/">Simple PHP Blog</a> verwendet (damals gab es noch kein Wordpress). Nach ein paar kurzen Experimenten mit Wordpress, Textpattern und co. habe ich dann schnell entschieden, alles selbst zu machen. Dabei bin ich bis jetzt geblieben.</p>
<p><img src="images/website_history.png" alt="history"></p>
<p>Das erste richtige Blog habe ich mit der Blogsoftware Simple PHP Blog umgesetzt. Damit bin ich auf den Geschmack gekommen und habe einige Anpassungen daran gemacht. Leider stand mir die Software mehr im Wege als dass sie mich unterstützt hat. Versuche mit anderen Systemen habe ich schnell aufgegeben.</p>
<p><img src="/images/aaronmuellerde2004.png" alt="aaron-mueller.de bis 2004"></p>
<p>Selbstgebaut mit PHP und <a href="http://www.smarty.net/">Smarty</a> als Templatte-Engine ging es dann in die zweite Runde. Die Daten wurden in einer MySQL Datenbank abgelegt. Für das Erstellen der Einträge habe ich mir ein rudimentäres Admin-Panel programmiert.</p>
<p><img src="/images/aaronmuellerde2006.png" alt="aaron-mueller.de bis 2006"></p>
<p>Etwas mehr als ein Jahre später entschloss ich mich, die Seite von Grund auf neu umzusetzen. Ebenfalls mit PHP und MySQL als Datenbank, doch mit dem <a href="https://github.com/eastridge/Picora">Picora Framework</a>. Klare Trennung von Model, View und Controller machten es einfach, neue Sachen auszuprobieren und bot der Codebase eine gewisse Stabilität. So baute ich verschiedene Kommentierfunktionen wie die Inline-Comments ein, die es ermöglichten Kommentare für jeden Absatz zu hinterlassen. Die Administration erfolgte wiederum über ein selbstprogrammiertes Admin-Panel. Bis zum Schluss habe ich es aber nicht geschafft, dieses komfortabel zu gestalten. Deshalb sind vermutlich so wenige Artikel entstanden, da das Erstellen sehr umständlich war (es fehlte eine Vorschaufunktion usw.). Dennoch war ich sehr zufrieden mit der Seite und sie hielt über 8 Jahre stand.</p>
<p><img src="/images/aaronmuellerde.png" alt="aaron-mueller.de bis 2015"></p>
<p>Die Planung für die jetzige Seite hat schon 2014 begonnen. Wirklich konsequent daran gearbeitet habe ich aber erst seit 2015 daran. Ich wollte dieses Mal (in meinen Augen) alles richtig machen und keinen schnellen Hack vom Zaun brechen. Deshalb haben manche Entscheidungen länger gedauert bzw. es fanden mehrere umfangreichere Umstrukturierungen während der Programmierung statt.</p>
<h1 id="technik">Technik</h1>
<p>Wichtig war mir vor allem das komfortable Editieren der Artikel. Ich hatte keine Lust mehr auf ein mickriges Textarea-Feld im Browser -- ich wollte die komplette Power meiner tagtäglich verwendeten <a href="https://www.gnu.org/software/emacs/">IDE</a> nutzen. Ich hatte zuvor einige Firefox- und Chrome-Plugins ausprobiert, mit denen sich rudimentär <a href="http://www.vim.org/">Vim</a>-ähnliches Verhalten simulieren lässt. Doch keines davon war annähernd brauchbar. Deshalb beschloss ich, das Editieren in meine IDE zu verlagern. Dazu programmierte ich mir mit <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/">elisp</a> einen MajorMode, mit dem ich nun all dies bewerkstelligen kann.</p>
<p><img src="/images/fuq_emacs.png" alt="fuq"></p>
<p>Als Datenbasis habe ich eine NoSQL-Datenbank verwendet, um flexibler zu sein und um mich selbst mehr damit zu beschäftigen. Anfangs wählte ich <a href="http://couchdb.apache.org/">CouchDB</a> aus. Ich wollte die komplette Webseite in CouchDB abbilden, was mit Views, Javascript und integrierter REST-Schnittstelle ideal erschien. Doch nach vielen Experimenten stellte ich fest, dass ich damit doch nicht so flexibel bin wie ich dachte. Deshalb sattelte ich auf <a href="https://www.mongodb.com/">MongoDB</a> um, und bin bis jetzt sehr zufrieden damit. Mit einem kleinen Migrationstool schob ich alles von der alten Webseite in eine Collection in MongoDB.</p>
<p><img src="images/website_architecture.png" alt="website architecture"></p>
<p>Für meinen Emacs MajorMode habe ich daraufhin einen kleinen Daemon programmiert, mit dem ich auf die Daten einfach zugreifen kann. Es gibt zwar ein etwas eingestaubtes Plugin für Emacs, um MongoDB-Queries abzusetzen, doch wollte ich vermeiden, den Datenbankserver ins Internet zu bringen und keine unnötige Komplexität beim Emacs Plugin erzeugen.</p>
<p>Die eigentliche Webseite wollte ich zuerst in <a href="http://clojure.org/">Clojure</a> programmieren, doch bin ich damit immer noch nicht <q>fluent</q> genug, was mich ausgebremst hat. Aus diesem Grund habe ich mich für Ruby/Rails entschieden, da ich mich damit sehr gut auskenne und schon einige Projekte zuvor umgesetzt habe. Ich zog auch das Erstellen von statischen Seiten in Betracht, doch war mir dies etwas <em>zu</em> starr und einige Ideen die ich mit der neuen Seite umsetzen will wurden dadurch unmöglich.</p>
<p>Auch habe ich eine brauchbare Suche implementiert, mit der die komplette Seite einfach und effektiv durchsucht werden kann. In früheren Projekten setzte ich dafür Elastic ein, was ich hier ebenfalls tat. Über einen Rails Task wird die Elastic-Datenbank immer auf dem aktuellen Stand gehalten.</p>
<h1 id="deployment">Deployment</h1>
<p>Über das Deployment habe ich mir einige Gedanken gemacht. Änderungen am Code sollen so einfach wie möglich sein, um meine Motivation zu erhöhen, regelmäßig Anpassungen und Erweiterungen vorzunehmen. Deshalb installierte ich an meinem Git-Repository ein post-receive Hook, der den kompletten Deployment-Prozess startet, wenn es Änderungen am Master gibt. Für das Deployment selbst habe ich auf Capistrano o.ä. verzichtet. Ein simples 5-Zeilen Bash-Script reichte vollkommen.</p>
<p>Zudem sollte die komplette Infrastruktur (Elastic, MongoDB, Rails, Socket-Daemon) lokal lauffähig sein, um einfacher zu entwickeln. Weiter noch: Ich wollte die komplett identische Infrastruktur mit einem einfachen Befehl aufsetzen, egal an welchem PC ich mich gerade befinde. Hierfür habe ich <a href="https://www.docker.com/">Docker</a> und <a href="https://docs.docker.com/compose/">Docker Compose</a> verwendet. Für erste Tests habe ich alles auf einem einfachen Digital Ocean Cloud Server gehostet, habe mich aber dann später entschieden einen richtigen VPS zu mieten, bei dem ich mehr fürs Geld bekomme. Dank Docker Compose ein Task von 30 Minuten.</p>
<p><img src="/images/sslaplus.png" alt="SSL A+"></p>
<p>Als Reverse Proxy habe ich <a href="http://nginx.org/">Nginx</a> verwendet, da ich mit der Konfiguration bereits gut vertraut bin. Besonders gelegen kam mir die Beta-Phase von <a href="https://letsencrypt.org/">LetsEncrypt</a>, zu der ich eingeladen wurde. So konnte ich endlich mein <a href="https://www.cacert.org/">CACert-Zertifikat</a> durch eines ersetzen, das wirklich jeder in seinem Browser installiert hat, nicht nur die Nerds.</p>
<p>Bei diesem Projekt wollte ich mir die Zeit nehmen, es so umzusetzen wie ich es für richtig halte. Viele Details wie die URL-Struktur, die Aufrechterhaltung <strong>aller</strong> alten URLs, die Navigation in den einzelnen Kategorien, die Farbgebung uvm. waren mir wichtig. Dies ist auch der Grund, warum es so lange gedauert hatte. Verbesserungsvorschläge, Lob und konstruktive Kritik nehme ich natürlich jederzeit gerne per <a href="mailto:mail@aaron-fischer.net">E-Mail</a> dankend entgegen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/229
2016-01-23T21:44:00+01:00
2016-01-23T21:44:00+01:00
Im Detail erklärt: Cronjobs mit systemd
<p>Mit systemd kommt ein sehr praktisches Werkzeug mit, das den klassischen Cronjob problemlos ablöst und durch ein paar nützliche Features erweitert. Es lohnt sich, einen Blick darauf zu werfen und zukünftig <a href="http://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd timers</a> anstelle von *cron zu verwenden.</p>
<p>Möchte man ein Script oder Programm zeitgesteuert oder in Intervallen starten, verwendet man meist einen der vielen verschiedenen cron-Programmen. Die meisten (anacron, cronie, ...) nutzen drei verschiedene Wege, um Cronjobs zu definieren: Der Aufruf von <code>crontab -e</code>, das Ablegen von Scripts in <strong>/etc/cron.(hourly|daily|monthly)</strong> oder das Einfügen von Cron-Files in <strong>/etc/cron.d/</strong>. Hierbei sind ein paar Details wie die Benennung der Files zu beachten, sonst startet der Cronjob schlicht nicht. Auch ist die Syntax exakt einzuhalten. Ob ein Cronjob erfolgreich lief, kann man mit Glück in <strong>/var/log/syslog</strong> nachsehen.</p>
<p>Eine Alternative sind <a href="http://www.freedesktop.org/software/systemd/man/systemd.timer.html">systemd timers</a>, die dem alten cron System ein paar Features voraus sind und etwas komfortabler zu bedienen sind. Scripte lassen sich nicht direkt mit systemd starten, es muss sich immer um ein <a href="http://www.freedesktop.org/software/systemd/man/systemd.service.html">Service</a>-File handeln. Dieses ist aber schnell angelegt und bringt ebenfalls ein paar Vorteile mit sich. Gehen wir im ersten Beispiel davon aus, dass einmal täglich ein Backup-Script gestartet werden soll.</p>
<p>Zuerst wird das Service-File unter <code>/etc/systemd/system/website-backup.service</code> angelegt. Das Backup-Script liegt unter <code>/usr/local/mongodb-backup.sh</code>.</p>
<pre><code>[Unit]
Description=Create a backup of the website data and push it to AWS
Wants=network.target
Requires=mongodb.service
After=mongodb.service
[Service]
Type=oneshot
ExecStart=/bin/bash mongodb-backup.sh
WorkingDirectory=/usr/local/
</code></pre>
<p>Schön an den Service-Files ist, dass Abhängigkeiten definiert werden können. So kann sichergestellt werden, dass eine Netzwerkverbindung besteht und dass die Datenbank auch läuft, von der das Backup erstellt werden soll. Ist eine Abhängigkeit nicht erfüllt, wird diese zuerst aufgelöst (in diesem Fall DB-Server starten und Netzwerkverbindung herstellen). <code>Type=oneshot</code> ist hier wichtig, damit systemd weiß, dass das Script normal beendet werden darf und nicht als Absturz gewertet wird. Anderenfalls geht systemd davon aus, dass der Service nach Beendigung des Scripts gecrashed ist. Mit <code>ExecStart=...</code> kann nun das Script angegeben werden, welches im Verzeichnis <code>WorkingDirectory=...</code> ausgeführt wird.</p>
<p>Um das Service-File zu testen, muss zuerst der systemd Daemon neu geladen werden, um das neue Service-File zu berücksichtigen.</p>
<pre><code>systemctl daemon-reload
</code></pre>
<p>Anschließend kann es wie gewohnt mit <code>systemctl</code> gestartet werden:</p>
<pre><code>systemctl start website-backup.service
</code></pre>
<p>Die Log-Ausgabe und den Status des Scripts kann mit <code>systemctl status website-backup</code> oder mit <code>journalctl</code> angesehen werden. Nun soll dieser Service in regelmäßigen Abständen ausgeführt werden. Dazu wird ein zusätzliches timer-file benötigt. Dieses muss den gleichen Namen wie das Service-File mit der Endung <code>.timer</code> haben. Die Datei <code>/etc/systemd/system/website-backup.timer</code> kann nun folgendermaßen aussehen:</p>
<pre><code>[Unit]
Description=Trigger the daily backup for the website
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
</code></pre>
<p>Mit <code>OnCalendar</code> lassen sich ähnlich wie bei cron <a href="http://www.freedesktop.org/software/systemd/man/systemd.time.html">Zeitintervalle</a> definieren. Das Flag <code>Persistent=true</code> sorgt dafür, dass das Service-File in jedem Fall in den angegebenen Intervallen ausgeführt wird, auch wenn es zum entsprechenden Zeitpunkt deaktiviert war.</p>
<p>Dieses Timer-File kann nun nach einem erneuten <code>systemctl daemon-reload</code> aktiviert und gestartet werden:</p>
<pre><code>systemctl enable website-backup.timer
systemctl start website-backup.timer
</code></pre>
<p>Um nun einen Überblick zu bekommen, welche Timer wann gelaufen sind und wann sie das nächste Mal ausgeführt werden, kann der <code>list-timers</code> Befehl für <code>systemctl</code> verwendet werden.</p>
<pre><code>systemctl list-timers
</code></pre>
<p>Der <code>[Timer]</code> Abschnitt lässt noch <a href="http://www.freedesktop.org/software/systemd/man/systemd.timer.html">einige interessante Optionen</a> zu. Ein Timer-File zum Aktualisieren des Caches könnte folgendermaßen aussehen:</p>
<pre><code>...
[Timer]
OnBootSec=5min
OnUnitActiveSec=10min
...
</code></pre>
<p>Der Timer ist nicht an einen fixen Zeitslot gebunden, mehr an die Laufzeit des Systems und des Services. Der Timer wird 5 Minuten nach dem Booten getriggert und alle 10 Minuten nach Start des Services.</p>
<p>Ein weiteres Beispiel könnte der Abgleich eines entfernten Verzeichnisses per rsync sein. Bei einem Host ist das kein Problem, doch wenn dies 1000 Hosts gleichzeitig machen, wird der Quellhost nicht optimal ausgelastet, da alle Hosts gleichzeitig anfragen. Für solche Fälle gibt es die Option <code>RandomSec</code>.</p>
<pre><code>...
[Timer]
OnUnitActiveSec=1h
RandomSec=30m
...
</code></pre>
<p>Somit wird der Service jede Stunde ein Mal ausgeführt, aber um 0-30 Minuten verzögert. Haben nun 1000 Hosts einen solchen Timer installiert, wird sich die Last auf den Quellhost optimal verteilen.</p>
<p>Weitere nützliche Optionen sind in der Manpage <code>man systemd.timer</code> zu finden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/228
2016-01-22T22:08:00+01:00
2016-01-22T22:08:00+01:00
Game Jams: JS13KGames 2015 (Reversed)
<p>In Anlehnung an <a href="http://dhmstark.co.uk/civclicker.html">CivClicker</a> wollte ich ein simples <q>Always Win</q>-Spiel mit Space-Theme machen. Da das Thema Reversed dazu nicht optimal passte, habe ich es mir ein wenig zurechtgebogen :) Ich wollte ebenfalls ein bisschen Simulation mit einbringen, so habe ich mich versucht so nah an <a href="https://en.wikipedia.org/wiki/Asteroid_mining">die Realität</a> anzulehnen wie möglich. Programmiert habe ich natürlich in JavaScript. Dieses Mal habe ich das modulare Framework <a href="http://minifiedjs.com/">minified.js</a> verwendet und anschließend mit dem <a href="https://developers.google.com/closure/compiler/">Google Closure Compiler</a> optimiert. Leider reichte die Zeit nicht mehr für ein paar wichtige Spielelemente wie Katastrophen, Crafting und Optimierung vom Equipment.</p>
<p><img src="/images/reverser.png" alt="reverser"></p>
<blockquote>
<p>Oh snap! The reverser is broken! Without this expensive part, the ship can't do any hyperjumps any more. We are stuck in the middle of nowhere, just a vast amount of black space. Luckily, there is an old space station near by, so we can buy the part for some money. But before that, some resources need to be harvest ...</p>
</blockquote>
<p>Gespielt werden kann das Spiel <a href="http://js13kgames.com/games/without-reverser/index.html">hier</a>. Auf der <a href="http://js13kgames.com/entries/without-reverser">Contest-Seite</a> gibt es wiederum mehr Informationen. Der Sourcecode ist <a href="https://git.okoyono.de/aaron/js13kgames-2015/src/master/release">hier</a> zu finden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/227
2016-01-22T21:50:00+01:00
2016-01-22T21:50:00+01:00
Game Jams: JS13KGames 2014 (The Elements)
<p>In diesem Jahr habe ich mich entschieden, etwas klassisches zu machen. Ein Spiel, das mich in meiner Kindheit sehr lange beschäftigt hat (damals noch mit dem GameBoy): Einen Tetris-Clone. Die Herausforderung mit 13.000 Bytes habe ich leicht unterschritten, ich wäre auch mit 5kb gut ausgekommen. So habe ich noch ganz verschwenderisch WAV-Files eingebaut :) Ich habe keinerlei Bibliotheken o.ä. verwendet und alle Grafiken sind generiert, so konnte ich den Sourcecode gut optimieren.</p>
<p><img src="/images/futris.png" alt="futris"></p>
<p>Das Spiel kann <a href="http://js13kgames.com/games/futris/index.html">hier gespielt</a> werden. Auf der <a href="http://js13kgames.com/entries/futris">Contest-Seite</a> gibt es noch ein paar Informationen. Der <a href="https://github.com/f0086/js13kgames-2014/tree/master/submission">Sourcecode</a> ist auf GitHub. Eine leicht modifizierte Version habe ich übrigens im Nitrado Webinterface versteckt. Mit dem entsprechenden Shortcut oder einem <q>futris()</q> auf der js-Console gehts los :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/226
2016-01-22T21:20:00+01:00
2016-01-22T21:20:00+01:00
Game Jams: BaconGameJam 05 (Lights out)
<p>Das Thema war <q>Lights out</q>, so beschloss ich zusammen mit Jonas und <a href="http://rubenartus.com">Ruben</a> ein kleines Labyrinth im Dunkeln zu machen. Programmiert wurde wieder in CoffeeScript, allerdings ohne jegliche Frameworks. So konnten wir die Größe minimal halten und mussten uns nicht erst in ein Framework einarbeiten. Leider fehlte noch etwas Polishing zum Schluss, so dass sich das Spiel leider ziemlich unfertig anfühlt, aber dennoch spielbar ist.</p>
<p><img src="/images/bacongamejam05.png" alt="bacongamejam05"></p>
<p>Die Spielbeschreibung:</p>
<blockquote>
<p>You woke up in the middle of the night, on a lost place. All you have is a candle and your brave guts. You are on your own. Nobody can help you finding the way out of the darkness. You have to walk through ugly drains, creep in smelling dungeons and dig through endless walls of mud. Keep an eye on the security cameras, don't step into the red lights. Good luck!</p>
</blockquote>
<p>Gespielt werden kann es <a href="https://arg-games.net/gamejams/bacongamejam05/index.html">hier</a>, der Code ist <a href="https://git.okoyono.de/arg/bacongamejam05-notsoaloneinthedark">hier zu finden</a>. Leider ist die Contest-Seite mit den Kommentaren und der Bewertung nicht mehr online. Wer etwas sucht, kann den Debug-Mode finden der das Licht anschaltet :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/225
2016-01-22T20:54:00+01:00
2016-01-22T20:54:00+01:00
Game Jams: Ludum Dare #31 (Space Diggers)
<p>Zusammen mit meinen beiden Brüdern Jonas und <a href="http://rubenartus.com/">Ruben</a> habe ich beim <a href="https://web.archive.org/web/20170920175841/http://ludumdare.com/compo/ludum-dare-31/?action=preview&uid=45557">Ludum Dare 31</a> mitgemacht. Das Thema war <q>Entire Game on One Screen</q>, so haben wir beschlossen ein Weltraum-Strategiespiel zu programmieren. Auf Basis von <a href="http://canvasquery.com/">CanvasQuery</a> haben wir mit der (für uns) kampferprobten Scriptsprache CoffeeScript in 48 Stunden etwas <a href="https://arg-games.net/gamejams/ld31/index.html">zusammengehackt</a>, das man ein Spiel nennen kann.</p>
<p>Die Beschreibung zum Spiel:</p>
<blockquote>
<p>You are repairing something on your ship when suddenly -- you snooze! You are drifting away from your ship, floating in space for hours until a small asteroid hits you. Luckily, your mobile base station kit is still intact, so you can start finding energy on this lost place to leave the planet as soon as possible. To collect energy, you need to build solar panels, but this isn't easy. Solar panels need a rare material, deep under the surface of this asteroid. You need to dig deep into the ground to find the needed materials for your power plants. This will bring up your energy meter. Good luck!</p>
</blockquote>
<p>Ein Timelapse meines PCs ist hier zu sehen:</p>
<iframe width="800" height="500" src="https://www.youtube.com/embed/VOkuFoV2Vr4" frameborder="0" allowfullscreen></iframe>
<p>Wer es spielen möchte, kann das <a href="http://arg-games.github.io/ld31/">hier</a> tun. Die Steuerung ist etwas gewöhnungsbedürftig und wir hatten leider keine Zeit mehr für eine anständige Erklärung. Dennoch ist es schaffbar und wer sich etwas mit Javascript auskennt, kann eine kleine Abkürzung über die Developer Console des Browsers nehmen :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/222
2016-01-18T23:22:00+01:00
2016-01-18T23:22:00+01:00
Privates: Here I am again!
<p>Es ist sehr lange nichts mehr auf meiner Website passiert, und auch die letzten Jahre haben die Anzahl meiner Blogbeiträge stark nachgelassen. Mein Leben ging rasant weiter, doch hier ist immer weniger passiert. Das möchte ich in Zukunft wieder ändern.</p>
<p><img src="/images/website_architecture.png" alt="Architektur"></p>
<p>Deshalb habe ich mich dazu entschlossen, eine neue Version meiner Webseite zu programmieren. Das war vor ca. zwei Jahren. Seitdem schraube ich mal mehr mal weniger an dieser Seite und an allem was daran angebunden ist. Wie auch die letzte Seite (die knapp 9 Jahre lang bestand) ist auch diese Seite 100% handgemacht. Die alte Seite habe ich in PHP programmiert, dennoch konnte ich den Programmcode ohne größere Probleme die letzten 9 Jahre am Laufen halten. Es war aber an der Zeit, PHP und MySQL über Bord zu werfen und durch zeitgemäße Programmiersprachen und Tools zu ersetzen. Ich hatte einfach keine Lust mehr, größere Features in PHP bzw. dem alten System umzusetzen. Die Details zur eingesetzten Technologie werde ich in einem separaten Post erläutern.</p>
<p><img src="/images/website_history.png" alt="History"></p>
<p>Ein Aspekt war mir beim Umbau wichtig: Persistenz. Durchschnittlich besteht Content im Web für 100 Tage, bis er sich ändert oder wieder verschwindet[<a href="https://www.youtube.com/watch?v=ubxWu0kne84">1</a>]. Dieser Statistik will ich entgegenwirken. Content der alten Webseiten und ebenso alle Links der letzten ~15 Jahre funktionieren bis heute. Mit Rewrite-Regeln und Legacy-Routes habe ich alle bestehenden Links an ihren neuen Platz umgelenkt. Und auch den Content habe ich entsprechend migriert und in die neue Struktur einsortiert.</p>
<p>Mit der neuen Suche ist nun das Auffinden von Content extrem einfach. Dies macht allerdings auch alten Content besser zugänglich. Sollten also Inhalte zum Vorschein kommen, die heute keine Gültigkeit mehr haben, würde ich mich über eine kurze <a href="mailto:mail@aaron-fischer.net">E-Mail</a> freuen. In den Bereichen <a href="https://aaron-fischer.net/essays">Essays</a> und <a href="https://aaron-fischer.net/projects">Projekte</a> wird es ebenfalls in Zukunft noch ein paar Anpassungen geben.</p>
<p>Auf eine Kommentarfunktion habe ich in der neuen Version der Webseite gezielt verzichtet. Dies wurde leider sehr wenig genutzt, aber machte spambedingt regelmäßig Schwierigkeiten. Ist ein Artikel wert zu diskutieren, bitte ich den Link auf <a href="https://news.ycombinator.com">HackerNews</a> oder <a href="https://reddit.com">Reddit</a> zu posten und dort zu kommentieren. Ich freue mich natürlich auch über eine E-Mail.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/221
2014-12-09T02:00:00+01:00
2014-12-09T02:00:00+01:00
Game Jams: Programming Contests 2014
<p>Auch in diesem Jahr habe ich trotz einiger privater <q>Verhinderungen</q> an drei Programmier-Jams/Contests mitgemacht. Dem <strong>js13kGames 2014</strong>, dem <strong>ClojureCup 2014</strong> und natürlich beim <strong>Ludum Dare 31</strong>. Diese Events wollte ich mir auf keinen Fall entgehen lassen!</p>
<h2 id="js13kgames-2014">js13kgames 2014</h2>
<p><img src="/images/js13kgames2014_logo.png" alt="js13kgames logo"></p>
<p>Dieser Contest findet jedes Jahr ein mal statt. Dieses Jahr zum dritten mal. Es ging darum -- wie in der Demoszene üblich -- mit so wenig Code so viel wie möglich zu machen. Hier ist die Einschränkung, wer hätte es gedacht, ein Spiel in 13kb JavaScript Code zu programmieren. Dazu zählen auch Assets wie Bilder, Audio, HTML, CSS etc. Ausschlaggebend ist die Größe des Zip-Archivs aller Dateien. Es gab noch einen Bonus-Punkt, wer das Thema <q>The Four Elements</q> in seinem Spiel mit einbringt. Ich habe mich dazu entschieden, das Thema komplett zu ignorieren. Gereicht hatte es dafür auf den 77. Platz.</p>
<p>Meine Einreichung ist <a href="http://js13kgames.com/entries/futris">hier</a> zu finden. Ich hatte mich dazu entschlossen, ein kleines Tetris-Spiel zu programmieren. Der Sourcecode (<q>Vanilla</q> JavaScript) ist 5.5kb groß (komprimiert 2.2kb), blieb also noch genug Platz, verschwenderisch WAV-Files mit einzubinden :) Es hätte also auch ein js4k sein können. Leider habe ich nicht alles umgesetzt, was ich mir so vorgenommen hatte. Hätte gerne noch verschiedene Levels und eine Highscore mit eingebaut, aber dafür hatte die Zeit dann nicht mehr gereicht. Der Code ist <a href="https://git.okoyono.de/f/js13kgames2014-futris">hier zu finden</a>.</p>
<p><a href="https://js13k-2014.bitfummler.de/">Hier gehts zum Spiel</a></p>
<h2 id="clojurecup-2014">ClojureCup 2014</h2>
<p><img src="/images/clojurecup2014_logo.png" alt="clojure cup logo"></p>
<p>Der <a href="https://clojurecup.com/">ClojureCup</a> fand dieses Jahr zum zweiten Mal statt. Hierbei ist die Hürde die Zeit. Es gibt 48 Stunden Zeit für das Erstellen einer Anwendung in Clojure oder ClojureScript (vorzüglich Websites). Es wurde für jeden der 100 Teilnehmer eine VM, diverse andere Web-Dienste und Lizenzen bereitgestellt, welche für den Contest benutzt werden sollen/können.</p>
<p>Ich war letztes Jahr schon mit dabei, allerdings bin ich kläglich gescheitert. Es fehlte mir etwas an Routine und Erfahrung mit Clojure, so dass ich viel Zeit mit dem <em>doc</em> Befehl verbracht hatte. Dieses Jahr wollte ich es besser machen. Deshalb fing ich gleich an, das Deployment zu sichern, so dass ich nicht kurz vor Schluss noch panisch den Webserver konfigurieren muss.</p>
<p>Mit meinem Projekt wollte ich alle bisher programmierten Spiele von <a href="http://www.ludumdare.com/">Ludum Dare</a> in einer übersichtlichen Seite mit einer Suchfunktion ausstatten. Die Idee bestand schon viel länger und zusammen mit <a href="http://rubenartus.com/">Ruben</a> hatte ich schon ein paar Ansätze unternommen das Projekt in die Tat umzusetzen, leider sind wir damit nie fertig geworden. Deshalb ergriff ich an diesem Wochenende die Chance und machte mich noch einmal dran. Ich bin verglichen zum letzten Jahr sehr viel weiter gekommen, allerdings bin ich auch dieses Mal gescheitert. Leider an ein paar Kleinigkeiten. Doch darum ging es mir dabei nicht. Es war ein sehr anstrengendes aber zugleich auch extrem tolles Wochenende! Es tat sehr gut, endlich wieder einmal mit einer brauchbaren Programmiersprache zu arbeiten. Ich habe vor, bei nächster Gelegenheit die noch verbleibenden Bugs zu beheben und dann auf meinem privaten Server zu veröffentlichen. Der Code ist <a href="https://git.okoyono.de/f/clojurecup2014-luduverse">hier zu finden</a>.</p>
<h1 id="ludum-dare-31-jam">Ludum Dare 31 (jam)</h1>
<p><img src="/images/ludumdare_logo.png" alt="ludum dare logo"></p>
<p>Zusammen mit meinen beiden Brüdern haben wir auch dieses Jahr wieder bei einem Ludum Dare mitgemacht. Hier gibt es ebenfalls das 48 Stunden Limit (Jam geht etwas länger). Wir hatten uns dazu entschlossen ein Strategiespiel zu programmieren, da das Thema <q>Entire Game on One Screen</q> war. Das passte ganz gut. Als Programmiersprache hatten wir CoffeeScript gewählt, da wir damit schon letztes Mal gut zurechtgekommen sind und die Sprache sich gut fürs schnelle <q>runterhacken</q> eignet. Neu dieses Mal war für uns die <a href="http://canvasquery.com/">CanvasQuery Lib</a> von Przemysław Sikorski, was ziemlich gut geklappt hatte. Zudem hatten wir unsere Desktops live auf Twitch.tv gestreamed. Es waren stets ~5 Zuschauer auf dem Stream, das sorgte nochmal für eine Extraportion Motivation.</p>
<p>Wir sind eigentlich ganz gut durchgekommen, leider fehlte wie so oft der letzte Schliff. Das sorgte dafür, dass das Spielprinzip nicht ganz klar wurde und so viele Spieler anfangs Probleme hatten das Spiel überhaupt zu verstehen. Darauf werden wir beim nächsten Mal besonders Wert legen.</p>
<p>Unsere Einsendung ist mittlerweile wieder offline, kann aber <a href="https://arg-games.net/gamejams/ld31/index.html">hier gespielt werden</a>. Der Code <a href="https://git.okoyono.de/arg/ld31-space-diggers">ist hier</a> Ein kleines Timelapse-Video habe ich dieses Mal auch erstellt.</p>
<p><center><iframe width="790" height="500" src="//www.youtube.com/embed/VOkuFoV2Vr4" frameborder="0" allowfullscreen></iframe></center></p>
<h2 id="weitere-contests">Weitere Contests</h2>
<p><img src="/images/bacongamejam_logo.png" alt="bacongamejam logo">
Letztes Jahr hatte ich zusammen mit meinen Brüdern beim Bacon Game Jam mitgemacht, dies wollten wir dieses Jahr wiederholen, haben es aber zeitlich nicht geschafft. Evtl. nächstes Jahr. Unsere Einreichung ist <a href="https://arg-games.net/gamejams/bacongamejam05/index.html">hier</a> zu finden.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/220
2014-04-28T04:00:00+02:00
2014-04-28T04:00:00+02:00
Im Detail erklärt: Dogecoin Primer
<p>Nach dem spektakulären Fall von MtGox ist das Thema Bitcoin für mich erst einmal auf Eis gelegt. Ich werde die Situation beobachten und abwarten. Doch ganz tatenlos zusehen wollte ich dann doch nicht und habe mir DogeCoin etwas näher angeschaut. Entstanden vor ca. drei Monaten auf Reddit, wird diese an Litecoin angelehnte Crypto-Währung momentan als Geheimtipp gehandelt. Auch wenn dies als Scherz begonnen hatte, steckt hier schon ein richtiger Markt dahinter. Und das tolle daran: Mining mit Grafikkarten ist zum jetzigen Zeitpunkt noch lukrativ! Wer sich also vom kindischen Logo nicht abschrecken lässt, hier eine knappe Anleitung für Dogecoin-Mining und allem was dazugehört.</p>
<p><img src="/images/20140428_dogecoin.png" alt="wow!"></p>
<h2 id="was-ist-dogecoin-was-ist-eine-kryptow-hrung">Was ist DogeCoin, was ist eine Kryptowährung?</h2>
<p><em>DogeCoin</em> ist eine Kryptowährung. Das bedeutet, dass Geld (Blöcke) durch kryptographische Verfahren erzeugt werden. Genauso wie bei Fiatgeld haben DogeCoins keinen Wert gegenübergestellt. Die Währung ist immer nur so viel wert wie Menschen bereit sind dafür zu bezahlen. Bei Bargeld hat man noch den Wert des Metalls, doch bei einer Kryptowährung ist alles digital und ohne realen Wert. Der Vorteil zum Fiatgeld von der Zentralbank ist aber der, dass es keine zentrale Einheit gibt. Ein Netz von Usern verwaltet gemeinschaftlich die Konten und Transaktionen. Ein weiterer Vorteil ist, dass keiner (technisch bedingt) mal schnell ein paar Scheine/Coins herstellen kann. Es existiert eine festgelegte Anzahl an Coins, die vom Netz erzeugt und verwaltet werden. Die Wahrheit (ist eine Transaktion gültig etc.) entscheidet das Netz -- völlig anonym über ein PeerToPeer-Protokoll. Ein Mitbestimmungsrecht erarbeitet man sich sprichwörtlich über Rechenleistung. Der <a href="http://de.wikipedia.org/wiki/Kryptow%25C3%25A4hrung">Wikipedia-Artikel</a> ist dazu sehr interessant.</p>
<h2 id="wie-funktioniert-mining">Wie funktioniert Mining?</h2>
<p>Um sich im PeerToPeer Netz vertrauen zu <em>erarbeiten</em>, muss man Hashsummen berechnen. Der Algorithmus des Systems sorgt dafür, dass immer eine konstante Menge an Coins von den Usern berechnet werden. Deshalb wird der Schwierigkeitsgrad ständig angepasst, je nachdem wie viele User mit wie viel Rechenkraft daran beteiligt sind Coins (Blöcke) zu produzieren. Ist also wenig los im Netz, ist der Schwierigkeitsgrad niedriger als bei Netzen mit vielen Usern.</p>
<p>Das Mining selbst besteht darin, den nachfolgenden Block in der globalen Blockkette zu berechnen. Genauer gesagt an einem Stellrad (Nonce) so lange zu drehen, bis der Hash (SHA-2 bei Bitcoin, Scrypt bei Dogecoin) des ganzen Blocks bestimmte Bedingungen erfüllt. Diese Bedingungen sind der Schwierigkeitsgrad an dem das System drehen kann um die Produktion von Blöcken konstant zu halten. Es geht also um reines raten. Wer öfter rät, hat mehr Chancen einen Treffer zu landen. Die Entlohnung wurde mit Version 1.6 auf eine konstante Summe angepasst. Davor war auch die Entlohnung in zufälliger Höhe.</p>
<h3 id="mining-pool">Mining-Pool</h3>
<p>Da das Finden einer passenden Nonce bei steigendem Interesse richtig schwer wird, gibt es Mining-Pools. Diese teilen den Suchraum auf die Teilnehmer auf und jeder sucht nur einen Teil ab. Die Teile werden auch <em>Shares</em> genannt. Je mehr Shares ein User für einen bestimmten Block bearbeitet, desto mehr wird er entlohnt. Es gibt verschiedene Abrechnungsmodelle, weiter unten die zwei bekanntesten. (Verschiedene Mining-Pools haben hierzu noch weitere angepasste Restriktionen, Gebühren und Berechnungs-Algorithmen).</p>
<p>In der <a href="http://www.doktorrf.com/dogecoin/pools.html">Liste der Dogecoin Miningpools</a> kann man sich für einen Mining-Pool entscheiden. Je mehr User in einem Mining-Pool mitarbeiten, desto schneller wird ein Block gefunden und desto schneller gibt es eine Ausbezahlung. Zu beachten sind auch die Gebühren und diverse Sonderheiten. Hier die Spalte <q>Notes</q> nicht außer Acht lassen.</p>
<p>Als Beispiel nehmen wir <a href="http://dogepool.pw/">dogepool.pw</a>, man kann aber jeden beliebigen anderen Pool verwenden. Je nachdem welches Ausbezahlungsmodell einem eher zusagt. Dazu einfach auf der Seite anmelden. Danach müssen wir einen neuen Worker anlegen. Das geht unter <a href="http://dogepool.pw/index.php?page%3Daccount&action%3Dworkers">My Workers im Benutzermenü</a>. Den Workernamen und das Passwort werden wir später für die Mining-Software brauchen.</p>
<h4 id="pay-per-share">Pay Per Share</h4>
<p>PPS oder Pay Per Share ist ein simples Entlohnungsmodell, bei dem der User für jeden bearbeiteten Share entlohnt wird. Hier fällt allerdings meist noch eine Gebühr an, da der Pool-Betreiber die Coins vorstrecken muss und evtl. bei einem Block sogar draufzahlt (wenn die Suche länger als gewöhnlich dauert).</p>
<h4 id="pay-per-last-number-of-shares">Pay Per Last Number of Shares</h4>
<p>Bei PPLNS wird pro Block abgerechnet. Man erhält für seine geleistete Arbeit an einem Block anteilig Coins. Das minimiert das Risiko für den Pool-Betreiber und im Gegenzug fallen meist keine Gebühren an. Um zu verhindern dass viele User nur einen Share berechnen und dann zum nächsten Pool wechseln (also wie PPS nur ohne Gebühren), werden die verdienten Coins nicht von allen Shares berechnet, sondern nur von den letzten 500000 vor dem Auffinden der passenden Nonce. Da keiner weiß wann die <q>letzten 500000 Shares</q> beginnen, lohnt es sich nur, wenn man konstant dabeibleibt.</p>
<h3 id="hardware-und-deren-mining-software">Hardware und deren Mining-Software</h3>
<p>Um sich beim Mining zu beteiligen, benötigt man entsprechende Hardware. Bei niedrigem Schwierigkeitsgrad kann man mit gewöhnlichen CPUs die Nonce berechnen bzw. kontinuierlich Hashes bilden und vergleichen. Dafür kann man <a href="https://bitcointalk.org/index.php?topic%3D55038.0">cpuminer</a> verwenden. Wird der Schwierigkeitsgrad größer, können mit Grafikkarten die Hashes effizienter berechnet werden (Pipelining etc.). Dafür empfiehlt sich <a href="https://github.com/cbuchner1/CudaMiner">cudaminer</a> (NVIDIA Grafikkarten) oder <a href="https://github.com/ckolivas/cgminer/releases/tag/v3.7.2">cgminer bis Version 3.7.2</a> für AMD/ATI Karten. Steigt der Schwierigkeitsgrad noch weiter an, kommt man nur noch mit spezialisierter Hardware aus (<a href="http://en.wikipedia.org/wiki/Field-programmable_gate_array">FPGAs</a> oder extra dafür gebaute ASICMiner, Block Erupter o.ä. -- welche es momentan aus gutem Grund nicht für scrypt gibt). Eine <a href="https://litecoin.info/Mining_hardware_comparison">Gegenüberstellung von Hardware zu Geschwindigkeit</a> (gemessen in 1000 Hashes pro Sekunde) findet man im Litecoin Wiki.</p>
<p>Als Beispiel gehe ich von einer ATI-Karte und cgminer aus. Nach dem Herunterladen der Software (die Version beachten!) müssen wir das cgminer Binary mit ein paar Parametern starten. Hier die entsprechenden Platzhalter durch die Werte ersetzen, die im Mining-Pool angegeben wurden (bei den Worker-Einstellungen).</p>
<pre><code class="bash">./cgminer -o http://dogepool.pw:3334 -u USERNAME.WORKER_NAME -p PASSWORD
</code></pre>
<p>Und schon kann es losgehen. Auf der Mining-Pool Seite kann man nun die aktuelle Hashrate und diverse andere Kennzahlen ansehen. Interessant sind hier die Werte <em>PPLNS Share Total</em> und <em>PPLNS Share Target Estimate</em>. Ersteres zeigt die Nummer der bereits berechneten Shares zum aktuellen Block an (die Nieten sozusagen). Die andere Zahl macht eine grobe Abschätzung, wann eine passende Nonce gefunden wird. Die Zeile darunter gibt Aufschluss darüber, wie viele Dogecoins vermutlich bei der aktuellen Runde für uns abfallen -- sollten wir bis zum Schluss mitrechnen.</p>
<h2 id="wallet">Wallet</h2>
<p>Um eigene Dogecoins zu speichern, wird eine <em>Brieftasche</em> benötigt. Dies wird <em>Wallet</em> genannt. Dies ist eine Client-Software, die es für die gängigsten Betriebssysteme gibt.</p>
<p>Nach dem <a href="http://dogecoin.com/">Download der entsprechenden Software</a> wird der Client auf den aktuellen Stand gebracht. Jeder User mit einem Wallet hat immer <em>alle</em> Transaktionen und Vorgänge im PeerToPeer Netz. Deshalb dauert der erste Start der Wallet-Software meist etwas länger. Beim ersten Start wird zudem ein Schlüsselpaar erzeugt. Der öffentliche Schlüssel ist die <q>Adresse</q>, an die Transaktionen gesendet werden können. Der private Schlüssel wird dafür verwendet, um eigene Aufträge zu signieren (Transaktion starten etc.). Er ist damit der Schlüssel zum Konto.</p>
<p>Bei einer Transaktion signiert der User seinen Auftrag mit seinem privaten Schlüssel und hängt den öffentlichen Schlüssel des Empfängers an. Da das Netz alle vorherigen Transaktionen und auch die Public-Keys kennt, kann seine Richtigkeit von allen anderen Clients überprüft werden (hat der User genug Geld, gibt es den Empfänger, etc.). Ist die Mehrheit der Meinung, dass die Transaktion legitim ist, wird sie ausgeführt und jeder Client speichert dies.</p>
<p>Um nun die beim Mining verdienten Dogecoins auf sein eigenes Wallet zu transferieren, genügt es, die öffentliche Adresse unter den Account-Details auf dogepool.pw zu hinterlegen. Unter <em>Automatic Payout Threshold</em> am besten 200 eingeben (der minimale Wert), dann wird -- sobald 200 Dogecoins erarbeitet wurden -- eine neue Transaktion gestartet.</p>
<h2 id="trading">Trading</h2>
<p>Was kann man aber nun mit Dogecoins anstellen? Zur Zeit werden Dogecoins auf Reddit als <em>Trinkgeld</em> (tips) verwendet oder aber um auf verschiedenen Plattformen Dogecoin in andere Kryptowährungen zu tauschen. Den aktuellen Kurs von Dogecoin bestimmt Nachfrage und Angebot. Auf <a href="https://www.cryptsy.com/">cryptsy.com</a> oder <a href="https://bter.com/">bter.com</a> kann man verschiedene Kryptowährungen handeln. Einen Graphen dazu gibt es auf <a href="http://bitcoinwisdom.com/markets/cryptsy/dogebtc">bitcoinwisdom.com</a> indem man auf Dogecoin umschaltet (oben auf <em>Markets</em>
klicken).</p>
<h3 id="dogecoins-kaufen">Dogecoins kaufen</h3>
<p>Es besteht die Möglichkeit, mit EUR Dogecoins zu kaufen. Eine gute Anlaufstelle dafür ist <a href="http://www.dogeforsale.com/trade/buy/eur">dogeforsale.com</a>.</p>
<h3 id="in-bargeld-verwandeln">In Bargeld verwandeln</h3>
<p>Das geht momentan noch nicht so einfach, aber ich vermute es wird nicht lange dauern bis es ein MtGox für Dogecoin geben wird (welches hoffentlich nicht in PHP geschrieben ist).</p>
<h2 id="abschluss">Abschluss</h2>
<p>Es macht Spaß bei einer so jungen Kryptowährung wie Dogecoin mitzuwirken und so leicht in die Welt der Kryptographie, PeerToPeer-Netzwerke, Trading und viele andere spannende Gebiete einzutauchen und dabei noch etwas Geld zu verdienen. Dies war nur ein ganz kleiner Überblick, es gibt noch unzählige weitere Themen zu denen ich hier kein Wort verloren habe. Auf diese stößt man allerdings schnell, sobald man sich etwas mit den angesprochenen Dingen beschäftigt. Viel Spaß damit!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/219
2013-08-12T00:00:00+02:00
2013-08-12T00:00:00+02:00
Netzkultur: Die Zertifikatslüge
<p>Seit der NSA-Geschichte habe ich das Vertrauen in die SSL CAs (Certified Authorities) komplett verloren. Ich gehe jede Wette ein, dass VeriSign, Equifax, GeoTrust usw. schon längst ihre Root-Zertifikate herausgegeben haben. Kann sich noch jemand an DigiNotar erinnern? So unwarscheinlich ist es nicht, dass jemand an das Root Zertifikat kommt. Erst Recht nicht, wenn dahinter eine Regierung/Organisation und viel Geld steht.</p>
<p>Für diejenigen, die sich damit noch nicht beschäftigt haben, hier ein kleiner Einblick in die Machenschaften von SSL-Zertifikaten:</p>
<p>Wenn man eine Webseite mit dem Browser öffnet und ein Schloss, ein grünes Feld oder ein vertrauenswürdig ausschauendes Schild in der Adressleiste erscheint, ist für die meisten klar: Jetzt ist die Verbindung sicher, keiner kann mein Passwort mitlesen. Manchmal kommt es vor, dass folgende Meldung erscheint:</p>
<p><img src="/images/20130812_notrust.png" alt="Browser trust fail"></p>
<p>Anstelle der Webseite wird eine bedrohliche Warnung angezeigt, in der die Rede von Hackern, falscher Identität und Vertrauen ist. Das ist für die meisten unverständlich. Warum wird der einen Webseite vertraut, aber der anderen nicht? Hat die Webseite womöglich einen Virus der auch mein iPad befallen kann?</p>
<p>Natürlich nicht. Dieser Mechanismus sorgt dafür, dass sich nicht jeder als google.de ausgeben kann. Es gibt dem Browser/User die Sicherheit, dass sich unter google.de auch wirklich google.de verbirgt und nicht ein gehackter Server in Russland.</p>
<p>Damit dieses System funktioniert, muss der Browser oder das Betriebssystem entscheiden können, welchen Webseiten er traut und welchen nicht. Den kryptographischen Teil ausgeschlossen (TLS), ist das Prinzip recht simpel: Das Betriebssystem bzw. der Browser bringt eine Reihe von Zertifikaten mit sich, die vom Browserhersteller (Mozilla, Google, Apple, ...) bzw. vom Betriebssystemhersteller (Microsoft, Apple, Debian, ...) als <q>vertrauenswürdig</q> auserwählt wurden. Welche das sind, kann man einsehen:</p>
<p><img src="/images/20130812_rootcas.png" alt="Browser Trust"></p>
<p>Alle Zertifikate in dieser Liste traut der Browser/Betriebssystem von Grund auf. Aber nicht nur diesen, sondern allen anderen die von einem in dieser Liste signiert wurden. Traut also Equifax Google und Google <a href="http://www.google.de">www.google.de</a>, wird <a href="http://www.google.de">www.google.de</a> automatisch auch vertraut (Web of Trust).</p>
<p><img src="/images/20130812_google.png" alt="Google"></p>
<p>Ähnlich hier auf meiner Seite. CACert traut mir. Ist das Root Zertifikat von CACert in deinem Browser als vertrauenswürdig eingebunden, traut dein Browser automatisch meiner Seite.</p>
<p><img src="/images/20130812_cacert.png" alt="CACert"></p>
<p>Um dieses Vertrauensverhältnis (Zertifikatskette) herzustellen, muss die jeweils darüberliegende Instanz der darunterliegenden ihr Vertrauen ausprechen. In meinem Fall hat also CACert meiner Domain aaron-fischer.net das Vertrauen gegeben. Für diesen <q>Service</q> wird von den meisten CAs (Zertifizierungsstellen) Geld verlangt (100 EUR oder 20.000 EUR im Jahr, je nach Einstellung(!)).</p>
<p>Was passiert nun aber, wenn einer der vielen Zertifizierungstellen bestochen/gezwungen/beraubt wird? Angenommen, Equifax gibt ihren privaten Schlüssel weiter, so dass jemand anderes außer Equifax Zertifikate ausstellen oder ungültig machen kann. So könnte dieser sich als google.de ausgeben und keiner würde es merken, da der Browser weiterhin Equifax vertraut. Und die verschlüsselte SSL-Verbindung würde zwischen deinem Browser und einem beliebigen anderen Server (der sich als google.de ausgibt) stattfinden. Zugegeben, es müssten ein paar Faktoren zusammentreffen (DNS-Einstellungen, ausreichend Hardware, sauber konfigurierter Reverse Proxy), aber für eine NSA sollte das kein Problem darstellen.</p>
<p>Also: Der Warnhinweis im Browser hat nichts damit zu tun, welche Inhalte sich auf der Webseite befinden. Er sagt nur aus, dass dein Browser der Webseite momentan nicht vertraut. Vertraut wird Equifax und GoDaddy (bzw. Microsoft, Google und Mozilla) statt realen Personen die man wirklich kennt und zu denen man echtes Vertrauen hat. Und die CA's sind natürlich wenig bestrebt, an der jetzigen Situation etwas zu ändern -- sie verdienen schließlich eine Menge Geld mit dem Verteilen von Vertrauen.</p>
<p>Was also tun? Eine Kombination aus <a href="http://web.monkeysphere.info/">Monkeysphere</a> (OpenGPG für den Browser) und <a href="https://keybase.io/f0086">keybase.io</a> (OpenGPG für jedermann; wer noch eine Invitation braucht, bitte melden, hab noch ein paar!), doch hier müssen die Browserhersteller den ersten Schritt machen. Ich bin gespannt wer sich hier als erstes traut :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/218
2013-05-13T02:00:00+02:00
2013-05-13T02:00:00+02:00
Text: Journaled vim
<p><a href="http://feitel.indeedgeek.de/">Florian</a> hat bereits in seinem Blog einen sehr <a href="http://feitel.indeedgeek.de/2013/4/journaled_vim/">lesenswerten und ausführlichen Artikel</a> zum Thema <q>Notizen mit Vim + Extras</q> geschrieben. Wer bereits wie wir schon alles mal durchprobiert hat und immer noch nicht zufrieden ist, sollte zuerst Florians Artikel lesen, es lohnt sich. Ich bin seit einigen Wochen ebenfalls auf <a href="https://code.google.com/p/vimwiki/">vimwiki</a> umgestiegen, da alle anderen Alternativen für mich nicht funktioniert hatten und simple Markdown Files ohne Links auf Dauer zu umständlich sind.</p>
<p>Da ich ständig irgend etwas aufschreiben/nachschlagen will/muss, und auf der Arbeit vimwiki auch als persönliche ToDo-Liste nutze, musste diese Lösung einfach und immer zur Hand sein. Deshalb habe ich ein Terminal mit geöffnetem Vim stets als <a href="http://i3wm.org/docs/userguide.html#_scratchpad">Scratchpad</a> Fenster offen (ich verwende <a href="https://www.youtube.com/watch?v=QnYN2CTb1hM">i3</a> als Window Manager) und kann es bequem mit [WIN]+- ein- und ausblenden.</p>
<p>Diesen Prozess habe ich automatisiert. Ein kleines Script <strong>~/bin/n</strong> prüft, ob bereits das Scratchpad offen ist und wenn ja, wird es in den Vordergrund gelegt. Wenn noch kein Scratchpad Fenster offen ist, wird ein neues Terminal geöffnet, Vim mit vimwiki und dem aktuellen Tag gestartet und auf das Scratchpad gelegt.</p>
<pre><code class="bash">#!/usr/bin/env sh
set -e
# Scratchpad already open?
if [[ `which i3-ipc` && `i3-ipc -p -t 4 | grep scratchpad | grep -v none` ]]; then
i3-msg scratchpad show 1> /dev/null
exit 0
fi
# Open a new scratchpad an start the vimwiki diary
i3-sensible-terminal -e vim -c VimwikiMakeDiaryNote &
while true; do
[[ `xdotool search --onlyvisible --pid $! 2> /dev/null` ]] && break
sleep 1
done
i3-msg floating enable 1> /dev/null
i3-msg move scratchpad 1> /dev/null
</code></pre>
<p>Natürlich lässt sich dieses Script auch in die <strong>~/.i3/config</strong> legen und beim Start gleich mit ausführen:</p>
<pre><code class="bash">exec ~/bin/n
</code></pre>
<p>Somit ist das private Wiki immer griffbereit. Was mir regelmäßig passierte, war, dass ich das Scratchpad total vergaß (da im Hintergrund) und mein PC ausschaltete. So gingen ungespeicherte Änderungen verloren. Damit mir das nicht mehr passiert, habe ich folgende Zeilen in meinem <strong>~/bin/off</strong> Script ergänzt:</p>
<pre><code class="bash">if [[ `which i3-ipc` && `i3-ipc -p -t 4 | grep scratchpad | grep -v none` ]]; then
i3-nagbar -t warning
-m "A Scratchpad is open, please close first."
-b "open pad" "i3 scratchpad show"
1>2 /dev/null
fi
sudo shutdown -h now
</code></pre>
<p>Sollte das Scratchpad noch offen sein, wird ein kleiner Hinweisdialog (gelbe Leiste am oberen Bildschirmrand) angezeigt, das mich davor warnt. Wirklich sehr praktisch :)</p>
<p>Da ich mein Wiki nicht nur an einem PC nutze, habe ich alles in einem Git-Repository und ein kleines Vim-Mapping, mit dem ich die Stände abgleichen kann. Damit komme ich eigentlich sehr gut zurecht.</p>
<pre><code class="bash">nnoremap <<leader>>wf :!cd ~/.fuNotes; git pull; git aa; git commit -m "changes"; git push<<cr>>
</code></pre>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/217
2013-05-04T00:00:00+02:00
2013-05-04T00:00:00+02:00
Game Development: Ludum Dare 26
<p>Letztes Wochenende (26.-29.4.2013) war wieder <a href="http://www.ludumdare.com/compo/">Ludum Dare</a>. Das ist eine Online-Competition, bei der es darum geht ein Spiel in 48 Stunden zu erstellen. Allein und ohne Vorarbeit. Just you and your code. (Es gibt auch eine <q>Jam</q>-Variante, bei der auch Teams mitmachen können, welche etwas länger geht). Wochen vorher entscheidet die Community durch Voting, an welchem Thema (ein einziges Wort) sich das Spiel orientieren soll.</p>
<p>Dieses Mal haben wieder sehr viele mitgemacht und es gab <a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview">2347 Einreichungen</a>. (Das Thema war beim ld26 <strong>Minimalism</strong>, also wieder mal ein sehr dehnbarer Begriff :) Diese werden nun die nächsten Wochen durchforstet und anschließend bewertet. Ich habe mich auch etwas durch die Einreichungen gewühlt und meine 10 Favoriten herausgefischt. Meine Bedingungen waren simpel: Sofort lauffähig unter Linux oder im Browser. Alles was nicht gleich getan hat oder nur unter Windows lief, habe ich mir nicht angesehen. Vermutlich habe ich dadurch einige tolle Spiele übersehen. Falls ihr eure Favoriten vermisst, schreibt es doch an die Seite oder als Kommentar.</p>
<p>Hier nun meine Top 10.</p>
<h2 id="platz-10">Platz 10: ::.::::::.</h2>
<p><em>von <a href="https://github.com/rwos">Richard Wosssal</a></em></p>
<p>Ein relativ simples Spiel, das mit an die alten Zeiten von Wolfenstein 3D und andere Labyrinth-Pseudo-2D Spiele erinnerte. Ziel ist es, den Ausgang (rosa Fläche) zu finden. Wird man von Gegnern berührt (farbige Würfel), verliert man Energie und das Bild fängt an unscharf zu werden. Die gelaufene Strecke wird aufgezeichnet und je länger man durch die Gänge irrt, desto eher hat man anhand seiner selbst gezeichneten Karte eine Ahnung davon, wo der Ausgang sein könnte.</p>
<p><img src="/images/ld26_10.png" alt="Platz 10">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=14690">entry</a>, <a href="http://r-wos.org/hacks/ld26/">play</a>, <a href="https://www.youtube.com/watch?v=qNkf9XoTaEk">timelapse</a></p>
<h2 id="platz-9-sky-tea">Platz 9: Sky Tea</h2>
<p>von <a href="https://soundcloud.com/lekky12">Aleksis Kavalieris</a></p>
<p>Recht schnelles Spiel, das etwas an Super Mario in den Wolken erinnert. Es spielt sich flüssig und die Animationen sind stimmig. Das Pixel-Artwork hätte noch etwas mehr Abwechslung gebraucht, aber das macht dem Spielspaß nicht wirklich viel weg. Minimalismus war ja Thema :) </p>
<p><img src="/images/ld26_09.png" alt="Platz 9">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=21270">entry</a>, <a href="http://www.kongregate.com/games/OysterKLAN/sky-tea">play</a></p>
<h2 id="platz-8-brave-hero">Platz 8: Brave Hero</h2>
<p>von <a href="https://twitter.com/Pixel_Indigo">Alex Stafeev</a></p>
<p>Ein ziemlich kurzes Spiel (mit Glück hat man es in ~20 Sekunden durch), doch die minimale Grafik ist echt super. Erst durch die Bewegung im Spiel kann man erkennen, was was ist, an welchen Stellen man springen muss und was Gegner sind. Ziel ist es wie bei so manch anderem Spiel, die holde Maid aus den Klauen des Bösen zu befreien.</p>
<p><img src="/images/ld26_08.png" alt="Platz 8">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=20260">entry</a>, <a href="https://dl.dropboxusercontent.com/u/55651497/BraveHero.jar">download (jar-file)</a></p>
<h2 id="platz-7-potato-the-destroyer">Platz 7: Potato The Destroyer</h2>
<p>von <a href="http://corymartin.net/">Cory Martin</a></p>
<p>Ein sehr schöner Platformer, der ebenfalls an Super Mario erinnert. Man spielt eine Kartoffel mit einer Pistole, die sich den Weg durch die Landschaft ballert und alles Gemüse das ihr im Wege steht eliminiert. Sieht alles sehr solide aus und es gibt sogar einen Endboss. Das Spiel selbst ist allerdings extrem schwer wie ich finde.</p>
<p><img src="/images/ld26_07.png" alt="Platz 7">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=3043">entry</a>, <a href="http://www.stencyl.com/game/play/18705">play</a>, <a href="http://www.youtube.com/watch?v=gFnVMzn-4SU">timelapse</a></p>
<h2 id="platz-6-tzatzatzauberer">Platz 6: Tzatzatzauberer</h2>
<p>von <a href="http://keksdev.de/">Christoph Schröder</a></p>
<p>Ein Spiel, das mich direkt an Nethack erinnerte. Man spielt einen Zauberer, der Erfahrung und andere nützliche Eigenschaften durch das Bekämpfen des Bösen (Hamster, Quadrate, Kobolde, Frösche, ...) erlangt. Irgendwo auf der riesigen dynamisch generierten Map soll es auch einen Endboss geben, habe ihn aber leider nicht gefunden.</p>
<p><img src="/images/ld26_06.png" alt="Platz 6">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=4439">entry</a>, <a href="https://googledrive.com/host/0B9Ss8mmLXjMlSmllU1Q5N3FrMGM/">play</a>, <a href="https://dl.dropboxusercontent.com/u/354581/ld26.jar">download (jar-file)</a></p>
<h2 id="platz-5-atom-that-matters">Platz 5: Atom That Matters</h2>
<p>von <a href="http://poly.gonum.free.fr/index.html">Leon Denise</a>, <a href="http://hannesdelbeke.blogspot.com">Hannes Delbeke</a>, Aurelia Binetti und Adrien Albertini</p>
<p>Eine Planetensimulation der etwas anderen Art. Man steuert (nur mit der Maus! Ohne Klicken, ohne Tastatur) einen kleinen Haufen Weltraumschrott, der sich um ein paar Planeten tummelt. Ziel ist es, so viel Schrott wie möglich aufzusammeln in dem man drauf zusteuert. Optisch sehr ansprechend und die Steuerung ist auch interessant.</p>
<p><img src="/images/ld26_05.png" alt="Platz 5">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=22982">entry</a>, <a href="http://poly.gonum.free.fr/atm/web/AtomThatMatters.html">play (unity)</a>, <a href="https://www.dropbox.com/s/jjkm4o552qi4cui/Linux%20Build.zip">download (linux)</a></p>
<h2 id="platz-4-hunter-hungry">Platz 4: hunter hungry</h2>
<p>von <a href="http://jonathanwhiting.com/">Jonathan Whiting</a></p>
<p>Ein sehr nett gemachtes Spiel, bei dem der Vater seinen Kindern Essen bringen muss. Er muss sich immer tiefer in einen Berg schlagen und Hasen jagen. Mit einem Multifunktions-Bogen bewaffnet kann man ganz im <a href="http://worms.wikia.com/wiki/Bungee">WORMS Bungee Style</a> durch die Luft schwingen und weiter in die Höhle vordringen. Optisch nicht 100% mein Geschmack, aber es macht sehr viel Spaß zu spielen!</p>
<p><img src="/images/ld26_04.png" alt="Platz 4">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=527">entry</a>, <a href="http://jonathanwhiting.com/ld/26/">play</a></p>
<h2 id="platz-3-the-parasite">Platz 3: The Parasite</h2>
<p>von <a href="http://www.jimgam.com/">Jimi Ahlgren</a></p>
<p>Ein Pixelspiel, das sich wie ein fertiges Spiel anfühlt. Man spielt ein kleines Quadrat, das sich durch die einzelnen Stages schlagen muss um den Ausgang zu erreichen. Dabei kann man das Level aufessen und sich wie bei <a href="http://supermeatboy.com/media/1/Super_Meat_Boy/">Super Meat Boy</a> fortbewegen. Die Levels sind (vermutlich?) generiert, zumindest haben sie einen zufälligen Charakter. Es gibt auch einen Endboss -- eine Kartoffel! Vorsicht, das Spiel macht süchtig :)</p>
<p><img src="/images/ld26_03.png" alt="Platz 3">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=17805">entry</a>, <a href="http://www.jimgam.com/ld26/">play</a>, <a href="http://www.youtube.com/watch?v=Vx5XsO9myM4&feature=youtu.be">walkthrough</a></p>
<h2 id="platz-2-towers-of-rage">Platz 2: Towers of Rage</h2>
<p>von den Brüdern <a href="https://twitter.com/sibachmann">Simon</a> und <a href="https://twitter.com/stbachmann">Stefan</a> Bachmann</p>
<p>Dieses Spiel hat mich gleich gefesselt. Allein die detailverliebte Pixelgrafik hat es verdient hier genannt zu werden. Man spielt einen von drei Kämpfern, die sich den Weg durch mehrere Level im <a href="http://www.abandonia.com/de/games/14106/Mad+TV.html">MadTV Hochhausstyle</a> erkämpfen müssen. Bei dem Spiel passt einfach alles. Sound, Grafik, Spielspaß, Details. Es sind sogar kleine Combos möglich, einfach ausprobieren.</p>
<p><img src="/images/ld26_02.png" alt="Platz 2">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=22760">entry</a>, <a href="http://robotality.com/html5/towersofrage/">play</a></p>
<h2 id="platz-1-toom">Platz 1: TOOM</h2>
<p>von <a href="http://www.toonormal.com/">Mike Kasprzak</a> und <a href="https://twitter.com/laufman">Derek Laufman</a></p>
<p>Ok, das beste zum Schluss: TOOM ist wirklich ein sehr gelungenes Point&Click Adventure, das einfach extrem Spaß macht zu spielen (nicht nur weil 3D-Drucker darin vorkommen :D). Man spielt einen Mann in einer Art Forschungsstation im Wald, der sich gerade eine Fernsehserie anschaut. Durch geschicktes Kombinieren von Gegenständen lassen sich wieder andere Dinge finden oder einen Mechanismus in Gang setzen. Ich hab über eine Stunde mit diesem kleinen Spiel zugebracht und am Ende auch den Sourcecode durchforstet um wirklich nichts auszulassen (kleiner Tipp: Der Ventilator hat eine entscheidende Bedeutung :). Die Atmosphäre ist ähnlich wie bei <a href="http://www.swordandsworcery.com/">Swort and Sworcery</a>, doch es kommt richtiges Lucas Arts (R.I.P.) Feeling auf, das ich früher bei diversen <a href="http://de.wikipedia.org/wiki/Script_Creation_Utility_for_Maniac_Mansion">SCUMM</a> Spielen hatte. Aber auch der Code selbst ist eine tolle Leistung; Hier wurde keine fertige Engine o.ä. verwendet, es ist (bis auf <a href="http://createjs.com/#!/CreateJS">create.js</a>) alles Hand
Crafted.</p>
<p><img src="/images/ld26_01.png" alt="Platz 1">
<a href="http://www.ludumdare.com/compo/ludum-dare-26/?action=preview&uid=19">entry</a>, <a href="http://sykhronics.com/toom/ld48/">play</a></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/216
2013-01-01T00:00:00+01:00
2013-01-01T00:00:00+01:00
Projekte: Adventskalender 2012 - Nachlese
<p>Das war er also, unser <a href="https://advent2012.aaron-fischer.net/">Adventskalender 2012</a> von <a href="http://feitel.indeedgeek.de/">Florian</a> und mir. Er ist noch ein paar Wochen unter den bekannten Adressen erreichbar, wer noch damit herumspielen möchte. Wir haben einige Gimmicks versteckt, die noch niemand gefunden hat (wir können es beweisen, wir haben jeden eingegebenen Befehl mitprotokolliert :).</p>
<p><img src="/images/20130107_adventheader.png" alt="Adventskalender 2012"></p>
<p>Wer sich alle Scripte noch einmal lokal auf der eigenen Festplatte/SSD anschauen möchte und das ein oder andere in seine eigene Sammlung mit aufnehmen möchte, kann dies gerne tun. Ein <a href="https://data.datenhalter.de/adventskalender2012.tar.bz2">komplettes Archiv mit allen Scripten</a> gibt es zum Downloaden.</p>
<p>Wir haben eine ganze Menge Arbeit in die Aufbereitung unserer Scripte und auch in die Oberfläche selbst gesteckt. Schon im Oktober hatten wir begonnen, Ideen zu sammeln, Scripte zusammenzutragen und die Web-Console zu programmieren. Wir hofften, vielen damit eine Freude zu machen. Dies wurde uns auch mit zwei Klicks auf den Flattr-Button gedankt.</p>
<p>Die Beteiligung insgesamt war allerdings etwas ernüchternd für uns. Insgesamt hatten wir 176 Aufrufe der Seite mit ~50 Sekunden Verweildauer im Schnitt. Pro Tag waren im durchschnittlich 5 Besucher auf der Seite und haben ein Türchen geöffnet. Feedback gab es leider fast keines.</p>
<p><img src="/images/20130107_adventstats.png" alt="Statistiken vom 1. bis 24. Dezember 2012"></p>
<p>Woran mag das gelegen haben? Am fehlenden iPad-Support? Am Weihnachtsstress? War es zu kompliziert? Haben wir vielleicht einen viel zu verschobenen Blickwinkel auf Software, die die Bedürfnisse der Allgemeinheit nicht im geringsten wieder spiegelt? Nicht einmal die eingefleischten Nerds, die immer noch meinen Blog lesen? Schreibt doch in den Kommentaren oder als Randnotiz, was wir im nächsten Jahr besser machen können. Was wünscht ihr euch für den Adventskalender 2013?</p>
<p>In diesem Sinne wünsche ich allen ein gutes und erfolgreiches Jahr 2013!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/215
2012-11-30T00:00:00+01:00
2012-11-30T00:00:00+01:00
Adventskalender: Adventskalender 2012
<p>Es ist mal wieder soweit. Der letzte Monat im Jahr kommt unaufhaltsam auf uns zu -- gefolgt von unzähligen analogen und digitalen Adventskalendern mit Gewinnspielen und anderem Kram. Da mussten <a href="http://feitel.indeedgeek.de/2012/adventskalender/">Florian</a> und ich natürlich wieder mitmachen!</p>
<p><img src="/images/20121130_ascii_tree.png" alt="ascii_tree"></p>
<p>Wir haben uns die letzten Wochen die Nächte um die Ohren geschlagen, um euch auch dieses Jahr einen Adventskalender der etwas anderen Art zu präsentieren: Da wir bekanntlich gestandene Terminal-Nerds sind, gab es für uns gar keinen anderen Weg als den Adventskalender in Form einer Shell zu machen.</p>
<p>Freut euch also auf viele kleine Scripte aus dem ~/bin/ Verzeichnis von <a href="http://feitel.indeedgeek.de/">Florian</a> und mir. Es geht zwar erst morgen los, doch ihr könnt euch natürlich schon jetzt mit der Console vertraut machen :)</p>
<p><strong>Bookmarken und jeden Tag Türchen öffnen:</strong></p>
<ul>
<li><a href="https://advent2012.aaron-fischer.net/">https://advent2012.aaron-fischer.net/</a></li>
<li>oder <a href="http://advent.indeedgeek.de/">http://advent.feitel.indeedgeek.de/</a></li>
</ul>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/214
2012-11-17T00:00:00+01:00
2012-11-17T00:00:00+01:00
Privates: if true then return true else return false end
<p>Eine nicht ganz repräsentative Diskussion mit meinen Arbeitskollegen hat ergeben, dass es anscheinend wohl wirklich schwer ist, jemanden anzutreffen, der Software mit Verstand und Enthusiasmus entwickelt. Dieser Umstand hat mich heute etwas beschäftigt und ich konnte ihn nicht wirklich glauben. Gibt es wirklich tatsächlich und leibhaftig Programmierer, die noch nie etwas von RubyOnRails gehört haben?</p>
<p>Die Verbreitung von Ruby und Python (und deren bekanntesten Web-Frameworks RubyOnRails und Django) hätte ich viel höher eingeschätzt -- zumindest unter den Webentwicklern. Ich bin davon ausgegangen, dass jeder web-affine Software-Entwickler mindestens irgendwann mal über Begriffe wie <q>Rails</q>, <q>Github</q>, <q>Codeigniter</q> oder <q>Ubuntu</q> gestolpert ist und sich dann interessiert gefragt hat <q>Was ist denn das?</q>. Ich erwarte ja nicht, dass jeder schon was mit Begriffen wie <q>Node.js</q>, <q>Coffeescript</q>, <q>MongoDB</q> oder <q>Yesod</q> anfangen kann. Aber man muss sich schon sehr verschließen, wenn man als Software-Entwickler noch nie ein Git-Repo gecloned hat.</p>
<p>Aber vielleicht ist das Umfeld, in dem ich mich bewege, ja nicht das eines <em>normalen</em> Software-Entwicklers? Ich habe eine Weile darüber nachgedacht, bin aber zu keinem Ergebnis gekommen.</p>
<p>Vielleicht hat das Problem auch etwas mit Perfektion, Selbstdisziplin, dem Anspruch an sich selbst und die Hingabe zum Beruf zu tun. Ich für mich habe mittlerweile unzählige Programmiersprachen und noch mehr Frameworks und Bibliotheken ausprobiert. Ich bin fast schon süchtig nach neuen Sachen. <a href="http://news.ycombinator.com/">HN</a> ist mein Facebook, auf dem ich regelmäßig versumpfe. Ich denke vermutlich über Dinge nach, die die meisten mit einem ungläubigen <q>...aha...</q> quittieren würden, wenn ich ihnen davon erzählen würde.</p>
<p>Mein Anspruch an einen Software-Entwickler, der seine Arbeit einigermaßen ernst nimmt, ist ein Mindestmaß an Neugier und Gespür für seine Programmiersprachen, Werkzeuge und Techniken. Die Wahrheit ist aber tatsächlich, dass sehr viel Software und Softwaresysteme regelrecht zusammengebastelt werden und nur durch einen dummen Zufall funktionieren. Es ist eigentlich erstaunlich, wie viel davon tatsächlich funktioniert.</p>
<p>Ist das vielleicht auch der Grund, warum StartUps so erfolgreich sind? Wenn sich doch mal ein paar durch einen glücklichen Zufall gefunden haben, die etwas mehr von Ihrem Beruf erwarten als nach 8 Stunden Eclipse-Klickerei wieder nach Hause zu dürfen, ist das schon ein Produktivitätsboost von 5000%.</p>
<p>Man merkt einem Menschen einfach an, wenn er etwas halbherzig und ohne eigenen Anspruch macht. Beim Software Engineering merkt man das wahrscheinlich einfach schneller. Ich vermute mal, bei Köchen oder anderen handwerklichen/kreativen Berufen ist das ähnlich.</p>
<p>Manchmal komme ich mir vor wie in einer Sendung <q>[Die Kochprofis - Einsatz am Herd][2]</q>. Sie fahren zu ihrem Einsatzort; der Hobby-Koch vom <q>Gasthaus zum Hirsch</q> zerfleddert gerade mit seinem stumpfen Brotmesser das aufgetaute Fleisch vom Vortag. In der Mikrowelle wird derweil die Vorspeise warm gemacht. <q>Die Spezialität des Hauses, nach unverändertem Rezept seit 25 Jahren!</q>, lässt sich Martin Baudrexel erklären. Das Fleisch noch schnell in das fünf Monate alte Frittierfett getunkt -- <q>So mag man das hier. Der Schnellimbiss-Chinese im Einkaufszentrum macht das auch so.</q>. Zum Nachtisch wird ein 19 Cent Joghurt in ein kleines Schälchen umgekippt, Hauptsache es sieht schön aus. Ralf Zacherl hat schon seit 5 Minuten die Facepalm-Geste eingenommen.</p>
<p>Auch wenn kochen und programmieren zwei unterschiedliche Disziplinen sind, erkenne ich da so manche Analogie :) Doch die ursprüngliche Frage muss damit ja nicht zwangsläufig etwas zu tun haben. Stimmt es wirklich, dass es so wenige Programmierer und Software-Architekten gibt, die ihren Beruf ernst nehmen? Und wenn es nicht so ist, sind diejenigen, die es doch tun mit ihrem aktuellen Job wirklich zufrieden? Wie ist das in anderen Berufen?</p>
<p>Ich weiß, das ist jetzt nicht wirklich repräsentativ, doch ich versuche es dennoch: Ich habe eine kleine Umfrage vorbereitet. Jeden Programmierer der bis hier her gelesen hat bitte ich sich <a href="http://1770431.polldaddy.com/s/kompetenzfragen">1 Minute Zeit zu nehmen und ein paar Klicks</a> zu machen. Die Auswertung gibts dann in einer Woche :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/213
2012-10-12T00:00:00+02:00
2012-10-12T00:00:00+02:00
Text: Why I One-Space
<p>Ok, auf HN kocht <a href="http://news.ycombinator.com/item?id=4645121">das Thema</a> wieder hoch: Wie beende ich einen Satz korrekt? Eigentlich ist dieses Problem schon sehr lange gelöst. Nach einem Satz folgt ein einziges Leerzeichen, wenn darauf ein weiterer Satz folgt. Zwei Sätze werden nicht mit <a href="http://stevelosh.com/blog/2012/10/why-i-two-space/">zwei</a>, drei oder gar einem <a href="http://rhodesmill.org/brandon/2012/one-sentence-per-line/">return</a> getrennt (so ein Schwachsinn!). Und wenn danach nichts mehr kommt, braucht man auch kein Leerzeichen mehr zu schreiben. So einfach ist das!</p>
<p>Aber warum schreiben dann trotzdem so viele Menschen diese unnötigen
Leerzeichen? Selbst die Vim Doku ist voll davon. Dafür gibt es vermutlich wieder mehrere Gründe: In USA wird dieser Stil in der Schule gelehrt -- meist von der old-school Generation, welche noch mit einer Schreibmaschine aufgewachsen sind. Eine Schreibmaschine sieht für jeden Buchstaben eine fixe Breite vor. Da dieser Umstand das Erkennen eines Satzes etwas schwerer macht, gab es damals Tasten für 1/2 und 1/4 Leerzeichen. Damit konnte man dem Text etwas typografischen Zucker einstreuen. Auf einem normalen 105 Tasten Keyboard gibt es diese Taste nicht mehr und man hat sich mit zwei Leerzeichen beholfen.</p>
<p>Bei Monospace Schriftarten, bei denen jeder Buchstabe gleich breit ist, kann ich das gut gemeinte Leerzeichen noch ansatzweise verstehen, doch stört mich da auch die Kluft zwischen den Sätzen. Zudem ist es syntaktisch einfach nicht korrekt. Beim Lesen will ich keine Pausen machen, man spricht ja nicht umsonst vom <q>Lesefluss</q>. Bei proportionaler Schrift kann ich die zwei Leerzeichen gar nicht verstehen, hier erledigt das Hinting, Kerning und die Schrift an sich das typografische Manko einer Typewriter Schrift.</p>
<p>Ein Argument hat mich aber etwas grübeln lassen. In den <a href="http://www.gnu.org/prep/standards/standards.html#Comments">GNU Coding Guidelines</a> steht folgendes:</p>
<blockquote>
<p>[...] Please put two spaces after the end of a sentence in your comments, so that the Emacs sentence commands will work. [...]</p>
</blockquote>
<p>Hier ist also das eigentliche Problem vergraben! Um automatisiert einen vollständigen Satz zu erkennen, genügen die definierten <a href="http://en.wikipedia.org/wiki/Punctuation">Satzzeichen</a> nicht. Fragezeichen und Ausrufezeichen sind klare Satztrenner. Der Punkt ist aber nicht kontextfrei. In Vim hat man ähnliche Probleme, hier kann man sich mit <code>set cpo+=J</code> aushelfen. Damit gibt man den zwei Leerzeichen die Bedeutung eines Satztrenners (mehr dazu unter <code>:help sentence</code>).</p>
<p>Für mich schreit das eher nach einem Quick Hack, um das Problem des kontextbehafteten Punktes zu umgehen. Das eigentliche Problem wird hier nur umgangen. Wieso ist das also so, dass man dem Punkt mehrere Bedeutungen gegeben hat? Als Abkürzungspunkt (z.B., usw. Dr. med., ...), als Ordnungspunkt (22. März, 2. Auflage) und als Dezimalgliederungspunkt (Kapitel 2.4, Kernel 2.6.32). Ich hab die Antwort darauf leider noch nicht gefunden. Vermutlich sah man damals dieses Problem einfach noch nicht oder man wollte nicht noch mehr Satzzeichen einführen.</p>
<p><img src="/images/20121012_satzzeichen.png" alt="Satzzeichen">
(Die gebräuchlichsten Satzzeichen in deutscher Sprache)</p>
<p>Aber um einen Satz zu erkennen gibt es noch <a href="http://en.wikipedia.org/wiki/Syntax">weitere Möglichkeiten</a>. Das heißt für mich im Umkehrschluss, dass man diese bescheuerten doppelten Leerzeichen eigentlich nur braucht, weil die Tools und Editoren noch zu primitiv sind. Was also tun? Warten bis die Programme besser werden oder weiterhin mit der Behelfslösung leben?</p>
<p>Wie macht ihr das? Trennt ihr Semantik und Typographie und lasst dann den Browser oder LaTeX die überschüssigen Leerzeichen entfernen oder behaltet ihr überall die Syntax konform und nehmt die schlechtere Toolunterstützung in Kauf?</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/212
2012-08-28T00:00:00+02:00
2012-08-28T00:00:00+02:00
Technologie: Die Angst vor der Console
<p>Ich habe schon unzählige Male erlebt, wie gestandene Programmierer sich vor einem schwarzen Fenster mit weißer Schrift fürchten. Meistens zu beobachten bei Windows-Usern mit rudimentären DOS-Erfahrungen.</p>
<p><img src="/images/20120829_console.png" alt="omg wtf?"></p>
<p>Da kommen dann meist Sprüche wie <q>Das nutzt man doch heutzutage nicht mehr</q>, oder <q>Aus dem Alter bin ich raus</q>. Auch vage Annahmen wie <q>Da bin ich mit einer GUI viel schneller, das ist doch alles viel zu umständlich</q> gibt es oft zu hören.</p>
<p>Besonders interessant wird es, wenn ein langjähriger Eclipse-Nutzer auf Git umsteigen will und im Internet nur Anleitungen für die Console findet. Schnell macht sich wieder die Angst breit und man distanziert sich gekonnt mit <q>Ist doch eh nur für die Linux-Freaks, Subversion tut wenigstens. Warum müssen wir nochmal Git verwenden?</q>.</p>
<p>Ich habe mich eine Zeit lang auch sehr schwer mit der Console getan, besonders was das Datei-Handling angeht. Nachdem ich dann eines Nachts mein komplettes ~/ mit einem unüberlegten <q>rm -rf</q> beerdigt hatte, war es vorbei mit dem Vertrauen. Ich vermute mal, so ein ähnliches Unglück ist jedem schon einmal passiert, der täglich mit einer Console zu tun hat. Doch ich denke genau das sensibilisiert die Nutzung der Console. Jemand, der noch nie etwas durch einen unüberlegten Befehl kaputtgemacht oder anderweitig versemmelt hat, hat vermutlich keinerlei Skrupel Befehle einzugeben, von denen er nicht weiß was sie bewirken.</p>
<p>Um <strong>gerne</strong> mit einer Console zu arbeiten muss man zuerst ein paar Hürden nehmen. (Leichter tut sich vermutlich jener, der früher <a href="http://www.web-adventures.org/cgi-bin/webfrotz?s=ZorkDungeon">Textadventures</a> gespielt hat, in <a href="telnet://moo.sindome.org:5555">MUD</a>s abgehangen hat oder seine Kindheit im <a href="http://www.psybnc.at/">IRC</a> verbracht hat)</p>
<p>(1) Die erste Hürde ist die Console an sich. Unter DOS gab es keinen Umweg um sie, mit Windows braucht man die Console heute nur noch um die eigene IP zu ermitteln oder mit dem <q>net</q>-Befehl rumzuspielen. Ansonsten ist sie weitgehend bedeutungslos. Was also mit einer Console unter einem Unix/Linux/BSD-System anfangen? Wie sind die Befehle, wie navigiere ich in den Verzeichnissen, was sind Berechtigungen, wie breche ich einen Befehl wieder ab, ... und was bringt mir das alles wenn ich doch den Windows Explorer und das Kontextmenü habe?</p>
<p>(2) Ist die erste Hürde genommen und die Console in den Grundzügen beherrscht, passiert meist das unvermeidliche: Man löscht versehentlich eine wichtige Datei (wo ist der <q>Bin ich mir sicher?</q>-Dialog?), sperrt sich mit falsch gesetzten Berechtigungen aus oder vermurkst mit grub seinen MBR. Man verflucht dieses elende Consolen-Gefrickel und ist schon kurz davor mit Thunar und gEdit eine längere Beziehung einzugehen.</p>
<p>(3) Ist diese Hürde auch überstanden, fängt man an seine Finger für die kritischen Befehle zu sensibilisieren. Bei <q>rm</q> oder <q>mv</q> atmet man nochmal durch bevor man dann zuversichtlich auf die [RETURN]-Taste drückt. bei <q>find</q> wird nicht lange überlegt, außer man nutzt den -exec Parameter. Bei <q>ls</q>, <q>tree</q> oder <q>less</q> kann nichts schlimmes passieren, egal wie sehr man sich anstrengt. Zur Not hämmert man eben ein paar mal CTRL+C. Zugleich hat man begriffen, wie Pipes funktionieren und welche Programme wie zusammengesteckt werden können.</p>
<p>Hat man dieses Level an Zuversicht erreicht, ist alles Andere nur Zeitverschwendung, total umständlich und nervig. Einige GUI-Programme gehen mittlerweile soweit, das Verhalten und den Workflow der Console nachzuahmen und sich möglichst nahtlos in den Consolen-Workflow zu integrieren (<a href="http://pwmt.org/projects/zathura/">zathura</a>, <a href="http://qsapp.com/">Quicksilver</a>, oder das <a href="http://alain.frisch.fr/soft_mozilla.html">Nostalgy</a>-Plugin für Thunderbird). Mal schnell ein 200MB Logfile nach ein paar Keywords filtern, nach Datum sortieren, einen Teil davon herausschneiden und die dritte Spalte davon zusammenzählen? Eine Sache von 20 Sekunden (incl. Rechenzeit!). Das ganze alle 30 Minuten machen und per E-Mail versenden? 30 Sekunden. Jedes grafische Tool wird mit ein paar Zeilen Bash-Script abgelöst und man kann sich endlich mit den wirklich anspruchsvollen Dingen beschäftigen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/211
2012-07-22T00:00:00+02:00
2012-07-22T00:00:00+02:00
Netzkultur: Warum Facebook auch stirbt
<p>Viele, die mit Facebook das Internet kennenlernen, sehen das Internet als <q>Facebook</q> mit Add-ons. Als ich das Internet kennenlernte, war für mich das Internet IRC mit Add-ons. Betrachtet man das Internet zu einem beliebigen Zeitpunkt, gibt es meist ein oder mehrere Monopole, um die sich alles dreht. Im Augenblick ist das Facebook und (erstaunlicherweise) Twitter.</p>
<p>Es gab eine Zeit, in der das Internet von Yahoo und AOL dominiert wurde. Jeder brauchte damals eine Yahoo-Adresse um an den vielen verschiedenen Services teilzunehmen (Foren, Newsgroups, E-Mail, ...). Redet heute noch jemand über Yahoo? Selbst del.icio.us haben sie verkackt und Flickr gerät auch langsam aber sicher in Vergessenheit. In letzter Zeit mal die Yahoo-Suche verwendet?</p>
<p>Es gab eine Zeit, in der <strong>jeder</strong> mit einem <a href="http://www.youtube.com/watch?feature=player_embedded&v=u404SLJj7ig#!">Netscape Browser</a> surfte. Eine wirkliche Alternative gab es nicht (Der IE ist und war noch nie eine Alternative). Heute kämpft der kleine Ableger Firefox schon mit sinkenden Nutzern.</p>
<p>Es gab eine Zeit, in der jeder auf MySpace war. MySpace hatte irgendwann mal so viele Accounts, dass es für jeden Amerikaner zweimal reichte. Jeder der etwas auf sich hielt hatte mindestens einen MySpace Account. Landet man heute durch Zufall auf einer MySpace-Seite, kommt man sich vor als würde man sich auf einem eBay-Shop befinden.</p>
<p>Es gab auch mal eine Zeit, da wollte jeder ein Wiki (nach der Zeit als jeder ein Forum wollte (nach der Zeit als jeder ein Gästebuch wollte)). Es schien <em>die</em> Lösung für alle Probleme zu sein. Das Web 2.0 in Reinform. Jede Firma, die aus mindestens zwei Personen bestand installierte sich mindestens ein Wiki um auch zum Web 2.0 zu gehören.</p>
<p>Es gab jüngst auch mal eine Zeit, in der jeder auf StudiVZ war. Ein paar Jungs wurden über Nacht zu Millionären und brachten <strong>alle</strong> auf ihre Plattform -- ob Student oder nicht.</p>
<p>Wie sieht es mit Facebook aus? Seit ein paar Jahren ist das der heiße Scheiß und jeder redet vom Social Media ohne eigentlich zu wissen, was das eigentlich bedeutet (Klaus, du weißt was es bedeutet, das weiß ich :). Glaubt ihr wirklich, Facebook sei das Ende? Nur weil man jetzt noch nicht weiß was danach kommt, ist das kein Grund anzunehmen, Facebook sei das Ende der Fahnenstange.</p>
<p>Facebook ist im Grunde eine Werbeplattform mit einem extrem lukrativen Nebeneffekt. Die Plattform lebt davon, dass Menschen so viel Informationen wie möglich von sich preisgeben, um diesen dann exakt zugeschnittene Werbung einzublenden oder diese Informationen an andere Firmen weiterzugeben. Eine Win/Win Situation.</p>
<p>Was würde passieren, wenn jemand den RaspberryPi mit einer Diaspora/StatusNet-Instanz kombiniert und das Resultat im Supermarkt verkauft? Ein dezentrales soziales Netz muss sich nicht mit Werbung über Wasser halten und muss an der Börse auch nicht die Backen aufblasen. Was passiert, wenn jemand etwas Besseres als Facebook entwickelt und das so dezentral und einfach macht wie E-Mail oder RSS? Es wird kein halbes Jahr dauern, da ist Facebook weg vom Fenster und etwas Neues wird kommen.</p>
<p><a href="http://www.technologyreview.com/news/427972/the-facebook-fallacy/">Michael Wolff</a> schreibt das ganz treffend: <em><q>Facebook isn't Google; it's Yahoo or AOL</q></em></p>
<p>P.s.: digg.com gibt es übrigens auch nicht mehr all zu lange.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/210
2012-07-16T00:00:00+02:00
2012-07-16T00:00:00+02:00
Technologie: Streamlined
<p>Mein letzter Artikel hier ist vom März. Und der war auch nur recht kurz. Der davor ist vom November 2011. Noch schlimmer. Am Layout und Design hat sich seit 2007 auch nicht mehr viel verändert. Es ist immer noch mein selbstgestricktes Teil mit PHP, das im Laufe der letzten 5 Jahre immer mal wieder notdürftig zusammengebunden wurde.</p>
<p>Wie immer mit solchen Projekten: Besser geht immer, perfekt ist es nie. Doch -- auch wenn es PHP ist -- diese Version meiner Webseite ist immer noch wartbar. Der Code ist übersichtlich und gut erweiterbar. Es gibt also keinen konkreten Grund, nochmal von vorn anzufangen, auch wenn die zugrunde liegenden Technologien nicht mehr die neusten sind.</p>
<p>Ein Frühjahrsputz wäre mal angesagt. meillo gab mir hierfür die entscheidende Inspiration. Er hat für seine Master-Thesis (<q>[The Modern Mail Handler][1]</q>) eine ziemlich alte Software nicht nur runderneuert, sondern aufgeräumt, alte Dinge entfernt und zeitgemäße Dinge eingebaut. Jeder andere hätte gesagt: <q>Lass das, schreib es lieber neu</q>. Doch die wirkliche Herausforderung besteht eigentlich darin, das bestehende zu verbessern. meillo hat sich dieser Herausforderung gestellt.</p>
<p>So will ich es mit meiner Webseite auch versuchen. Angefangen habe ich damit, in dem ich das SVN-Repository -- welches ich schon seit mehreren Jahren über <q>git svn</q> bediene -- zu einem richtigen Git-Repository migriert habe. Der Build-Prozess habe ich über einen Post-Commit Hook an das Repository angehängt, somit wird jeder Commit augenblicklich live. Zudem habe ich alles was HTTP war rausgeschmissen und auf HTTPS umgestellt. Alles (bis auf extern verlinkte Bilder o.ä.) wird jetzt konsequent per HTTPS ausgeliefert. (<a href="http://www.cacert.org/index.php?id=3&lang=de">Installiert das CACert RootCA!</a>) In diesem Zuge habe ich mich auch endlich von lighttpd verabschiedet und auf nginx umgeschwenkt. Sobald es eine Paketversion mit SPDY-Support gibt, werde ich allen Content per SPDY ausliefern.</p>
<p>Codetechnisch habe ich bisher nur einen Fehler gefixt (die Annotations funktionieren nun auch mit modernen Browsern :) und einiges rausgeworfen. Delicious ist schon lange tot und Flattr hat sich für mich nie richtig gelohnt und hat sich nur hässlich ins HTML eingewebt.</p>
<p>In diesem Sinne wird es evtl. demnächst noch ein paar optische Veränderungen geben und vielleicht sogar regelmäßigen Content.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/209
2012-03-29T00:00:00+02:00
2012-03-29T00:00:00+02:00
Gedanken: Tut nicht so als wüsstet ihr was ihr da tut!
<p>Es ist schon interessant zu sehen, wie sich das Internet entwickelt; Eigentlich hat niemand damit gerechnet, dass uns dieses Konstrukt so allumfassend vereinnahmt und überrollt. Keiner hat das vorausgesehen. Außer ein paar Spinner vielleicht. Und nun stehen wir da: Keiner weiß so recht, wie man damit umgehen soll. Jeder hat zwar eine grobe Vorstellung, aber seien wir mal ehrlich: Niemand weiß wirklich wie das ganze Anzupacken ist.</p>
<p>Vor dem Internet war alles klar geregelt. In jedem Land gibt es einen Gesetztestext und wenn der nicht aussagekräftig genug ist, entscheidet ein Richter wie dieser zu interpretieren ist. Das Internet passt nicht in dieses Muster, auch wenn sehr viele sehr angestrengt versuchen, die bekannten Muster darauf zu adaptieren. Leider geht das nicht so einfach -- denn das Internet ist etwas ganz anderes.</p>
<p>Im Internet geht es nicht um Ländergrenzen, Copyright oder Benutzeridentifikation. Das Internet ist da um zu teilen und uns etwas weiter zusammenzubringen (so ist zumindest mein subjektives Verständnis davon). All die Anstrengungen den Status Quo der analogen Welt im Internet aufrecht zu erhalten sind gescheitert und werden es weiterhin. Es ist nicht nur ein anderes Medium, sondern etwas ganz anderes.</p>
<p>Die einen meinen sie hätten <q>es</q> im Griff, wüssten wie das Internet tickt und wie es in fünf Jahren aussähe. Einige vertrauen dem Internet komplett und gehen mit Facebook und Twitter komplett darin auf. Andere haben Angst und versuchen mit allen Mitteln gegen alles Neue anzukämpfen. Wieder andere haben sogar so viel Ehrfurcht und Zweifel vor <q>diesem Internet</q>, dass sie es nicht mal anrühren. All das ist berechtigt, doch sollten wir uns alle klar sein: Die Menschheit macht hier gerade einen Zeitaltersprung durch, bei dem keiner das Ende kennt und auch keiner jemals zuvor eine solche Situation durchgemacht hat. Diese Situation ist für <u>alle</u> neu. Auch für die Musikindustrie, Großkonzerne, Entscheidungsträger, Gesetzesverabschieder und andere Staatsorgane.</p>
<p><strong>Edit</strong>: Auch ganz nett und irgendwie passend zum Thema ist dieser Artikel von <q>der Netzgemeinde</q>: <a href="http://ccc.de/de/updates/2012/drehbuchautoren">Antwort auf den offenen Brief der Tatort-Drehbuchschreiber</a></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/208
2011-11-18T01:00:00+01:00
2011-11-18T01:00:00+01:00
Text: Die perfekte Lösung -- wieder einmal.
<p>Ich bin stets bemüht (wie man so schön sagt) eine für mich akzeptable Lösung zu finden. Was das Editieren von Text angeht, bin ich schon mindestens ein Jahrzehnt auf der Suche nach <u>der</u> Lösung. Ich hätte ja gerne ein <a href="http://www.vim.org/">Vim</a>, programmiert in <a href="http://www.gnu.org/s/emacs/">Lisp</a>, mit nahtloser <a href="http://www.eclipse.org/">GUI-Integration</a>, dem <a href="http://manual.macromates.com/en/bundles">Bundle-Konzept</a> und den <a href="http://www.google.de/search?um=1&hl=de&safe=off&nord=1&q=textmate+file+explorer">File-Explorer</a> von Textmate und das ganze dann als <q>Plugin</q> für <a href="https://chrome.google.com/webstore/search/vim?q=vim">jede erdenkliche Texteingabe</a> auf allen Computern an denen ich Text eingeben oder bearbeiten muss. Leider gibt es einen solchen Editor nicht und ich vermute mal, das wird in naher Zukunft auch so bleiben.</p>
<p><img src="/images/20111118_vim.png" alt="Inception!!!"></p>
<p>Deshalb habe ich in den letzten Jahren so einiges ausprobiert und die ein oder andere Behelfslösung geschaffen. Grund warum ich dem ganzen jetzt einen Blogeintrag widme war ein Thread auf <a href="http://news.ycombinator.com/item?id=3251743">HN</a>, in dem jemand <a href="https://bitbucket.org/sjl/dotfiles/src/tip/vim/.vimrc">seine .vimrc</a> veröffentlicht hatte.</p>
<p>Als ich mir das Monster ansah, war ich irgendwie beruhigt: Ich bin nicht der einzige, der nach der perfekten Lösung sucht. Nur ging Steve einen anderen Weg: Er bog so lange an Vim herum, bis es halbwegs seinen Ansprüchen genügte. Ich habe seine Konfiguration nicht ausprobiert, ich glaube ich würde mich mit seinen Settings sowieso nicht zurechtfinden. Ich sah es eher als nette Anregung, die ein oder andere Option mal nachzuschlagen. (Wer sich übrigens einmal mit Vim beschäftigen möchte, dem seien <a href="https://aaron-fischer.net/artikel/kategorie/vim-mastery">meine Vim-Mastery Artikel</a> ans Herz gelegt).</p>
<p>Viele Interessanter waren aber die Kommentare dazu: Dabei ging es hauptsächlich um Ansätze zur Lösung des <q>überall die gleiche Config</q>-Problems. Für mich habe ich es so gelöst, dass ich die wichtigsten Konfigurationsdateien in meiner Dropbox abgelegt habe. Symlinks zeigen dann von <em>~/</em> auf die in der Dropbox. Damit bin ich eigentlich immer recht gut zurechtgekommen. In meinem neuen Job habe ich allerdings mit einigen Servern zu tun, so dass ich dort nicht überall einen Dropbox-Client installieren kann bzw. überall meine Konfigurationsdateien herum kopieren will.</p>
<p>Mir schwebte da schon seit längerem eine Vision im Hinterkopf; ein simples Script das für mich den Benutzeraccount so konfiguriert wie ich das brauche. Gebe ich die Kontrolle des Servers an jemanden anders ab, soll natürlich die ursprüngliche Konfiguration wiederhergestellt werden. <a href="http://news.ycombinator.com/item?id=3252120">Einer auf HN</a> hat den ersten Schritt schon so gemacht wie ich mir das gewünscht hatte:</p>
<pre><code class="c">wget -qO - bit.ly/newbox | sh
</code></pre>
<p>Simpel, einfach, funktional. Leider ist wie immer meine Situation etwas komplexer: Beispielsweise die Datei <em>~/.ssh/config</em> ist logischerweise auf der Arbeit eine ganz andere wie bei mir Zuhause. So sollte es auch bleiben. Teile ich mir einen Account mit einer anderen Person, will ich natürlich keine der Dateien in <em>~/.ssh/</em> <q>publizieren</q>. Nicht nur Dateien an sich sind ein Problem, auch einzelne Zeilen. Auf der Arbeit brauche ich Dank Cygwin ein paar mehr Einträge in meiner $PATH Variable die in der Datei <em>~/.zshrc</em> gesetzt ist. Es gibt natürlich einige Systeme wie Puppet, die solche Probleme lösen, doch das ist mir alles viel und zu groß und zu mächtig und zu unhandlich.</p>
<p><strong>An die SysOps da draußen: Wie habt ihr das Problem gelöst, wenn ihr es gelöst habt? Das würde mich mal brennend interessieren.</strong></p>
<p><em>Ach ja: Diesen Blog-Beitrag habe ich natürlich in Vim geschrieben und am Ende in den Browser kopiert. Das <q>Mein Editor everywhere</q>-Problem habe ich also auch noch nicht im Griff.</em></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/207
2011-11-06T00:00:00+01:00
2011-11-06T00:00:00+01:00
Netzkultur: Sammelsurien
<p>Schon lange nichts mehr hier geschrieben. Eigentlich wollte ich das jetzt öfter machen, doch irgendwie war jedes Mal wieder etwas anderes wichtiger oder ich habe es nicht für würdig empfunden es der Welt kund zu tun. Um diesem Teufelskreis zu entgehen, gibt es heute einen ganz unspektakulären Blogbeitrag ohne große Recherche oder Tiefsinn (wobei, vielleicht doch ein bisschen).</p>
<p><center><img src="/images/20111106_firstworldproblems.jpg" alt="First World Problems"></center></p>
<p>In den letzten Wochen habe ich mich von zwei Web-Diensten verabschiedet, weil sie mir nicht mehr zeitgemäß schienen oder weil sich die negativen Veränderungen gehäuft hatten.</p>
<p>Seit etlichen Jahren schon verwalte ich meine Bookmarks mit verschiedenen Web-Diensten. Angefangen hat es mit <a href="http://sourceforge.net/projects/scuttle/">Scuttle</a>, einem PHP-Projekt, mit dem Bookmarks verwaltet werden können. Einige Dinge hatten mich daran gestört und als dann Delicious richtig hipp wurde, bin ich mit Sack und Pack umgezogen. Dort blieb ich einige Jahre und es hatten sich so einige Bookmarks angesammelt.
(ungefähr 2500). Kurzzeitig hatte ich auch mal Mr. Wong verwendet, doch das war irgendwie nicht mein Ding. Delicious machte in den letzten Monaten und Jahren einige Veränderungen durch und lies dies seine Benutzer deutlich spüren. Die letzte <q>Rettung</q> des Projekts ist nun eine komplette Reimplementierung von Delicious, welche aber noch nicht mal im Ansatz das Beta-Stadium erreicht hat. Deshalb beschloss ich, meine Bookmarks einem zuverlässigeren und besseren Dienst anzuvertrauen und meine Wahl fiel auf <a href="https://pinboard.in/">pinboard.in</a>.</p>
<p>Als ich dort meine Bookmarks von Delicious importiert hatte, entdeckte ich etwas, das ich die letzten Jahre schon befürchtet hatte: ca. 350 Bookmarks endeten mittlerweile im Nichts. In Delicious hatte ich davon nichts mitbekommen.</p>
<p>Ein ähnliches Drama offenbarte sich bei meinem zweiten größeren Umzug in diesem Jahr. Seit es den Google Reader gibt, hatte ich ihn verwendet. Zu Beginn der RSS-Ära noch mit nervigen Desktop-RSS-Readern herumhantiert, war ich Gott froh über den Google Reader. Endlich mal ein anständiges Tool das überall verfügbar war und sich gut bedienen ließe. Dabei blieb es dann aber auch etliche Jahre -- richtig neue Features gab es eigentlich nicht. Bis vor einer Woche: Der Google Reader wurde an das neue Google CI angepasst und dabei erlitt es dasselbe Schicksal wie Delicious: Es wurde unbenutzbar für mich. Alles verhielt sich langsamer und träger, einige Funktionen verschwanden einfach und das Design war auch nichts für mich.</p>
<p><a href="http://feitel.indeedgeek.de/">Florian</a> erlitt ähnliche Qualen und beschloss, den Feedreader zu wechseln. Ich tat es im gleich und wechselte zu <a href="http://tt-rss.org/redmine/">Tiny Tiny RSS</a>, einer PHP-Anwendung, die im Prinzip dasselbe wie der Google Reader macht. Die Installation war recht problemlos und meine Feeds von Google Reader konnte ich auch importieren. Doch hier erwartete mich ein ähnliches Schicksal: 41 Feeds waren nicht mehr existent -- die Hälfte davon von Freunden die ich persönlich kenne. Das hatte mich schon ziemlich erstaunt. In Google Reader herrschte stets die heile Welt.</p>
<p>Das stelle ich mir nun zwei Fragen:</p>
<ul>
<li><p>Die Features und Funktionen von ttRSS und pinboard schlagen um Welten die der von mir verwendeten Vorgänger. Ich wusste es nur nicht. Hätten mich Google und Yahoo äh AVOS Systems nicht mit ihrem Downgrade belästigt, hätte ich mich vermutlich nicht nach einer Alternative umgesehen. Geht es mir hier vielleicht mit anderen Dingen auch so und ich merke es nur noch nicht?</p></li>
<li><p>Das Internet mit seiner nicht zu bewältigenden Menge an Informationen wird mir zunehmend zum Verhängnis. Ab und zu komme ich mir vor, als würde ich Informationen wie eine gigantische Schneekugel einsammeln und vor mir herschieben. Die Sucht nach Informationen ist enorm, doch ich komme mit dem Verarbeiten nicht mehr hinterher. Eine Stunde auf <a href="http://news.ycombinator.com/">HN</a> oder <a href="http://www.reddit.com/">Reddit</a> und der Berg wächst weiter. Oft denke ich: <q>Hochinteressant, aber keine Zeit zu lesen, mach ich doch ein 'todo'- oder 'toread'-Tag dran</q>. Ähnliches spielt sich in meinem Feedreader ab. Leider bleibt es meistens dabei und die Information verschwindet tief in meiner Bookmarkverwaltung oder als <q>gesternter</q> Artikel. Geht es nur mir so, oder kämpft noch jemand mit diesem <a href="http://first-world-problems.com/">first world Problem</a>?</p></li>
</ul>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/206
2011-09-11T00:00:00+02:00
2011-09-11T00:00:00+02:00
Gedanken: Ein Versuch, die Motivation von Open Source zu erklären
<p>Der Grund, warum ich leidenschaftlich programmiere hat mehrere Gründe. Zum einen ist es diese absolute Gleichheit und Fairness, mit der man unermüdlich konfrontiert wird. Jeder startet mit denselben Grundvoraussetzungen. Es wird niemand bevorzugt, weil er einen Anzug trägt und viel Geld hat oder benachteiligt, weil er einen langen Bart und eine verschlissene Jeans trägt.</p>
<p>Compiler und Interpreter kann sich jeder kostenlos aus dem Internet herunterladen und installieren. Auf den meisten Computern sind bereits eine Hand voll davon installiert. Überall wo ein Browser läuft, wird eine mächtige Programmiersprache bereits mitgeliefert. Den geeigneten Texteditor gibt es ebenfalls gratis dazu. Damit hat jeder Programmier die gleichen Voraussetzungen und <u>jedes</u> Programm beginnt mit einer leeren Datei in einem einfachen Texteditor. Keiner kann sich eine bessere Startposition erkaufen oder irgendwoher stehlen. Jeder fängt gleich an. (Mal abgesehen von Biblitheken o.ä.)</p>
<p><img src="/images/20110911_opensource.png" alt="xkcd">
<a href="http://xkcd.com/743/">xkcd #743</a></p>
<p>Was man dann daraus macht, hängt vom Programmierer ab. Man kann sich daran versuchen, die perfekte Symphonie zu komponieren oder man wirft mit Farbeimer um sich wie die Blue Man Group. Der Charakter des Programmierers spiegelt sich in seinem Code wider, so fällt auch schnell auf, wenn der eine Dinge vom anderen abkupfert. Natürlich gibt es auch den Mainstream der Best Practices, der versucht vordefinierte Formen zu liefern, so dass möglichst viele mit dem Code klar kommen. Für welchen Weg man sich entscheidet oder ob man von allem etwas nimmt, kann jeder für sich selbst entscheiden.</p>
<p>Das Programmieren im Allgemeinen fängt eigentlich schon viel früher an. Man hat eine Idee oder sucht eine Lösung für ein bestimmtes Problem. Man schleicht um das Problem, schaut es von allen Seiten an und versucht es zu knacken. Das Gefühl, die <q>perfekte</q> Lösung gefunden zu haben, ist dann als hätte man gerade den Weg aus einem richtig schweren Sudoku gefunden. Es gibt immer unzählige Möglichkeiten und Lösungswege; Stürzt man sich direkt auf das Problem, stellt man meist irgendwann fest, dass ein anderer Weg sinnvoller gewesen wäre. In solchen Fällen muss man dann eine harte Entscheidung treffen: Zurückrudern, weitermachen oder umbauen? Oft ergibt sich auch während der Programmentwicklung ein besserer Weg, da man das Problem besser verstanden hat und deshalb die Lösung besser vor Augen hat. Das ist auch der Grund, warum die meiste Software mehrere Evolutionsstufen (Versionen) durchlaufen, bis sie stimmig und performant sind.</p>
<p>Grundvoraussetzung dazu ist natürlich, dass man seine Werkzeuge die man verwendet verstanden hat und damit umgehen kann. (Würde man einem Zahnarzt trauen, der seine Instrumente nicht im Griff hat?) In den allermeisten Fällen ist nicht der Computer schuld, sondern der Programmierer hat seine Wünsche nicht korrekt geäußert -- oder nicht gewusst wie man sie äußert. Es hat aber nicht nur etwas mit Wissen und Ausdrucksfähigkeit zu tun, sondern auch sehr viel mit Kreativität und dem Mut seine Gedanken zu verwirklichen. Feige Programmierer erkennt man daran, wenn sie stets auf altbewährtes setzen und Probleme immer nach dem gleichen Muster zu lösen versuchen. Nicht jedes Programm zählt Zahlen in Zellen und Spalten zusammen. Es gibt so unglaublich schöne Software, die nicht nur von außen schön aussieht, sondern auch als Programmcode ein Genuss sind sie zu lesen.</p>
<p>Die theoretische Informatik versucht seit Jahren <a href="http://de.wikipedia.org/wiki/Halteproblem">vergeblich zu beweisen</a>, dass die Anzahl der möglichen Programme endlich sei, schafft es aber nicht. Das ist ein schönes Gefühl -- das Gefühl unbegrenzter Möglichkeiten.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/205
2011-06-30T00:00:00+02:00
2011-06-30T00:00:00+02:00
Netzkultur: Von der Fernsehgesellschaft zum Content-Provider
<p>Je länger ich das Internet verwende und darin lebe, desto mehr werden mir die Zusammenhänge, Trends und Veränderungen klar. Vermutlich geht es den meisten meiner Leser auch so: Wir sind eine Generation, die praktisch von Anfang an dabei war, als das Internet entstand. Wir wissen genau, wie sich Bandbreite im zweistelligen KBit-Bereich anfühlt und sind mit Fachtermini wie ROFL oder RTFM bestens vertraut -- denn <q>wir</q> haben es erfunden.</p>
<p><img src="/images/20110630_web_usage.png" alt="Web usage"></p>
<p>Doch wir sind nicht der Mainstream, an dem man irgend etwas ablesen könnte. Wir sind bestenfalls die, die sich Gedanken über die Probleme von morgen machen. Der Rest der Welt bekommt davon schlicht nichts mit.</p>
<p>Meine Eltern sind mit dem Fernseher aufgewachsen. Der reine Konsum von Information war allgegenwärtig. Man schaute in die Röhre und lies sich von dem berieseln, was das Hörzu Programmheftchen vorgab. Wer etwas auf sich hielt und es sich leisten konnte, kaufte sich einen zeitgesteuerten Videorecorder und entschied selbst, wann er was ansehen wollte. Das war aber vermutlich eine sehr geringe Minderheit.</p>
<p>Wir sind mit Computern und dem Internet aufgewachsen. Es war von Anfang an klar, dass es um geben und nehmen geht. Anfangs noch über das Usenet, dann über (oft in Perl geschriebene) Web-Foren wurde über technische Dinge diskutiert. Es ging aber fast immer darum, irgend welche Probleme zu lösen.</p>
<p>Später, als dann aus den Gästebüchern und Kontaktformularen Blogs wurden, rückte der Selbstdarstellungs-Charakter immer mehr in den Vordergrund. Es ging mehr darum, sich im Web zu präsentieren und sich ein Portfolio aufzubauen. Die Wiki-Phase vereinte dann die Selbstdarstellung mit dem <q>For the greater good</q>-Gedanken. Je mehr Menschen das Internet nutzten, desto mehr konsumierten den eigenen Text auf Wikipedia und Co. (und man wurde sogar zitiert oder komplett kopiert!).</p>
<p>Die Web-Foren hatten im Laufe der Zeit immer und immer mehr Features. Erst waren es nur private Nachrichten (holy shit, Nachrichten ohne Email!), dann das Anlegen von Buddies, Bilder hochladen usw. Daraus entstanden dann schnell unzählige Portale für so ziemlich jeden Lebensbereich in 20-facher Ausführung. Jeder wollte ein Portal haben, die Gier nach den meiste <q>Membern</q> fand ihnen Höhepunkt. Verständlicherweise überlebten nur einige wenige den anfänglichen Hype, der gescheiterte Rest dümpelt vermutlich noch heute auf vergessenen Webspace-Hostern herum.</p>
<p>Mit digg.com gab (ja, gab) es kurzweilig eine weitere Möglichkeit, ganz groß herauszukommen, ohne eine große Userbase zu haben. Man brauchte nur die richtige Headline und etwas Glück.</p>
<p>StudiVZ setzte auf dem üblichen Portal-Gedanken auf , doch da die Seite nach kurzer Zeit eine so unglaublich große Masse an Menschen an sich zog, wollte plötzlich jeder <q>drin</q> sein. Ursprünglich war das Portal nur für Studenten gedacht, doch schnell wurden die Felder <q>Hochschule</q> und <q>Studiengang</q> in der Eingabemaske für so ziemlich alles missbraucht. Kurse und Gruppen wurden zu Sammlerobjekten (wer hat die meisten Gruppen) umfunktioniert.</p>
<p>Als dann Facebook in die westliche Welt überschwappte, gab es die lang ersehnte general purpose Plattform für alle. Mit dem parallel wachsenden Smartphone-Markt war der perfekte Nährboden geschaffen, um der breiten Masse zu zeigen, dass das Internet keine reine Konsum-Apparatur ist.</p>
<p>Dabei bahnt sich (aus meiner Sicht) ein Problem an: Für Menschen, die mit Facebook das Internet kennenlernen, haben die persönlichen Daten keinen Wert. Eltern legen ohne nachzudenken einen Facebook-Account für ihr Baby an (inklusive Bilder und Youtube-Videos natürlich), geben sich keine Mühe bei der Passwortvergabe und geben bereitwillig all ihre Daten der ganzen Welt preis. Es ist fast aussichtslos, dieser Generation Dinge wie RSS oder den Vorteil von semantischen Daten zu erklären. Gruppen wie LulzSec oder Anonymous versuchen (versuchten) mit drastischen Mitteln auf diese Problematik hinzuweisen. Leider versteht genau die Zielgruppe für die sie gedacht ist, die Botschaft nicht.</p>
<p>Vielleicht braucht es ja noch 10 weitere Fälle wie bei Sony oder der CityBank, um das Thema Datensensibilisierung in den Köpfen des Mainstreams publik zu machen. Ich weiß es nicht. Vielleicht ist das der einzige Weg, man lernt ja bekanntlich am besten aus Erfahrungen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/204
2011-03-27T22:00:00+02:00
2011-03-27T22:00:00+02:00
Privates: No Route to Host
<p>Heute ist es Zeit für dich, Abschied von meinen Daten und mir zu nehmen. Du warst mir ein treuer Begleiter über fast einem Jahrzehnt. Anfangs hab ich dich überall mit hingeschleppt und dich mit fremden Netzen verkuppelt. Damals warst du hipp und neu, alle haben bewundert wie rank und schlank du doch bist. Sie haben sich nach dir umgedreht und gesagt <q>so klein, aber so viel Power und Potential!</q>. Natürlich hattest du auch Neider, die meinten, dass man mit dir sowieso nichts Ernstes anfangen kann. Sie drohten mir mit üblen Abstürzen nach durchzechten Nächten. Du hast mir gezeigt, dass mehr in dir steckt!</p>
<p><img src="/images/20110328_nas1.jpg" alt="Shuttle 1"></p>
<p>Im Laufe der Jahre hab ich so einiges mit dir gemacht und oft hattest du es nicht leicht mit mir. Du musstest jedes Betriebssystem ertragen, meist sogar mehrere gleichzeitig. Von BeOS bis Slackware hast du alles mitgemacht. Nächtelanges Kernel-Compilieren, exotische Peripheriegeräte oder Windows XP: du warst dir zu nichts zu schade.</p>
<p>Ich weiß, ich war nicht immer fair zu dir. Das eine mal, als ich dich ganz unvorbereitet vom Netz genommen habe, tut mir leid. Ich stand einfach voll auf der Leitung und hab dich mitgerissen. Das war aber kein Grund, mich tagelang anzuschweigen. Das ich dich daraufhin zur Dateiablage verdonnert hatte, nimmst du mir hoffentlich nicht übel. Auch, dass ich dich alles doppelt ablegen lassen hatte, war nur zu deiner Sicherheit, das weißt du ja.</p>
<p>Jahrelang warst du eng verbunden mit meinen Daten und ein paar meiner engsten Freunde. Jede Nacht hast du dich mit meinem Kollegen in Frankfurt abgestimmt, tagsüber hast du dich bereitwillig von mir füttern lassen.</p>
<p><img src="/images/20110328_nas2.jpg" alt="Image Alt Text"></p>
<p>Als du dann älter wurdest, wusste ich nicht mehr so recht, wie ich mit dir umgehen sollte. Du verhielst dich ab und an sehr merkwürdig und manchmal traute ich dir nicht mehr über den Weg. Auch konnte ich mit dir nicht mehr die Dinge machen, die man heute eben so macht. Die ständigen Operationen und dein immenser Verschleiß an Lüftern war irgendwann nicht mehr tragbar. Es ist jetzt einfach Zeit für mich, den Host zu wechseln.</p>
<p>Der Neue kann dich zwar nicht ersetzen, doch ist er um einiges pflegeleichter. Dich werde ich nun in den Keller abschieben. Dort wirst du dahinvegetieren, bis du an einem sonnigen Samstag beim Frühjahrsputz auf den Müll fliegst. Einen Organspendeausweis habe ich natürlich für dich ausgefüllt.</p>
<p>Lebe wohl und mach es gut mein treuer Begleiter.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/203
2011-03-09T23:00:00+01:00
2011-03-09T23:00:00+01:00
Privates: Thailand: Ein Résumé
<p>Die letzten drei Wochen habe ich in Thailand (Asien) mit meiner Freundin und zwei wirklich guten Freunden (Markus und Nicky) verbracht. Das war nicht nur für mich dringend nötig -- einmal richtig aus dem Alltag ausbrechen und verschwinden. Die Wochen (und Monate?) zuvor gab es in meinem Leben nicht viel mehr als die Arbeit; ich befand mich meist im Dauerstress. Das zehrte an meinen Kräften, physisch wie auch psychisch. Da war ein Urlaub ohne jegliches Notebook oder Handy genau das Richtige.</p>
<p><img src="/images/20110310_strand.jpg" alt="Strand"></p>
<p>In Bangkok angekommen gab es für uns erst einmal einen gewaltigen Kulturschock. Das Klima und auch die Menschen sind dort <a href="http://maps.google.com/maps?f=q&source=s_q&hl=de&geocode=&q=bangkok&aq=&sll=15.870032,100.992541&sspn=16.600278,19.753418&g=thailand&ie=UTF8&hq=&hnear=Bangkok,+Thailand&ll=10.336536,101.634521&spn=8.509156,9.876709&t=p&z=7">unten</a> ganz anders. Alles ist eine Spur gelassener, wärmer, runder, speckiger, klebriger, aber auch freundlicher. Man gewöhnt sich erstaunlich schnell an so manches, vorausgesetzt man geht unvoreingenommen an alles heran.</p>
<p><img src="/images/20110310_rucksack.jpg" alt="Rucksack"></p>
<p>Wir haben so vieles gesehen und erlebt, dass ich hier nicht jede Einzelheit berichten kann, aber ein paar Eindrücke und Gedanken möchte ich dennoch schreiben. Bangkok war eine unglaubliche Erfahrung. Die Menschen leben (für unsere Verhältnisse) erschreckend chaotisch. Aus einer Straße mit zwei Spuren werden kurzerhand vier Spuren gemacht, die abenteuerliche Stromverkabelung stellt die VDE100 komplett in den Schatten und an Hinweisschilder hält sich sowieso niemand. Es scheint fast so, als würde man mit Absicht genau das Gegenteil machen was vorgeschrieben ist. Aber trotzdem funktioniert irgend wie alles. Es schien für mich so, als würden die Menschen gegenseitig aufeinander aufpassen. Die Menschen machten den Eindruck, dass sie glücklicher und in Harmonie mit sich selbst und ihrer Umgebung leben.</p>
<p><img src="/images/20110310_buddah.jpg" alt="Buddha"></p>
<p>An jeder Ecke fand man einen kleinen Gebetstempel mit Opfergaben, selbst in Taxis war fast immer ein kleiner Buddha auf dem Armaturenbrett. Sie falten die Hände, bevor sie Geld annehmen und die allermeisten sind stets höflich, freundlich und hilfsbereit. Niemand wird alleine stehen gelassen. Diese Kultur hat mich schon schwer beeindruckt.</p>
<p>Eines wurde mir in Thailand ziemlich deutlich bewusst: Ich bin reich -- nicht nur was den Lebensstandard angeht, sondern auch reich an Erfahrung, Wissen, und Bildung. Für die meisten Thai ausserhalb der größeren Städte ist Englisch nicht selbstverständlich und selbst für Shop-Besitzer ist 5x60 Bath ohne einen überdimensionierten Taschenrechner nicht lösbar. In unserem Alltag selbstverständliche Dinge wie Copyright, Recycling, Müllentsorgung oder der Respekt vor der Umwelt scheint in solchen Entwicklungsländern noch nicht durchgedrungen (oder noch nicht umsetzbar) zu sein. Hier wird alles kopiert und gefälscht, der Müll wird zu Haufen zusammengekehrt und angezündet.</p>
<p>Das Bewusstsein, in einem Staat wie Deutschland von enormem Wohlstand zu leben, bekommen man erst deutlich zu spüren, wenn man eines der Länder in Äquatornähe mit eigenen Augen gesehen und erlebt hat. Ich zahle dafür gerne meine Steuern.</p>
<p>Die Einstellung der Thais zum Leben, ihren Mitmenschen und ihrer Umgebung hat mich dann aber schon etwas nachdenklich gemacht. Ich hatte zudem das erste Mal seit langem die Möglichkeit, über die vergangenen Monate zu reflektieren. Schon während meiner Abschlussarbeit hatte ich begonnen zu arbeiten, und danach blieb selten die Gelegenheit, über das Vergangene nachzudenken. Ich wollte beispielsweise etwas über mein Thesis-Thema bloggen oder ein paar Projekte aus meinem Studium vorstellen, bin aber seit über einem halben Jahr nicht dazugekommen dies zu tun. Ähnliches merke ich jetzt auf der Arbeit; hier purzeln die Feature-Requests schneller herein als ich sie umsetzen kann. Da bleibt keine Zeit mehr, Code-Reviews zu machen oder intensiv über eine Problemstellung nachzudenken und den besten Weg zur Umsetzung zu finden. In solchen Situationen beneide ich die Lebensweise der Thai.</p>
<p><img src="/images/20110310_aaron.jpg" alt="Aaron"></p>
<p>Vielleicht war es auch das Buch <a href="http://www.lovelybooks.de/autor/Mitch-Albom/Dienstags-bei-Morrie-63529064-w/">Dienstags bei Morrie</a> von Mitch Albom, dass mir etwas die Augen geöffnet hat. Das Leben ist zu schade, um es unbemerkt an sich vorbeiziehen zu lassen. Wenn man nur noch auf seine Umwelt reagiert, statt aktiv einzugreifen, hat man nicht mehr viel vom Leben. Voll und ganz auf die Dinge konzentrieren, die für mich wichtig sind und diese dann konsequent(er) verfolgen ist deshalb mein Ziel für dieses Jahr.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/223
2011-01-18T15:10:00+01:00
2011-01-18T15:10:00+01:00
Websites: aiv.de
<p>Für die Firma <a href="http://aiv.de/">AIV</a> habe ich als Angestellter der Firma <a href="http://asz-group.de">ASZ-Group</a> die Produktpräsentations-Seite erstellt. Die Webseite wurde mit Rails umgesetzt. Besonderes Augenmerk wurde auf die Such- und Filterfunktion gelegt, da der Artikelstamm mehrere Tausend ähnliche Produkte beinhaltet, die sich nur durch einzelne Attribute unterscheiden. Die darunterliegende Datenbasis musste aufwändig aufbereitet werden. Für die Produktspezifikationen wurde DSL entwickelt, mit der Informationen aus der Produktbeschreibung extrahiert werden konnte.</p>
<p>Die Produkte teilen sich in drei Produktsparten <q>car</q>, <q>mobile</q> und <q>home</q> auf. Zudem ist der Einstieg über einer der vielen geführten Marken möglich. Die Suchfunktion berücksichtigt dies ebenfalls. Diese Seite ist als Werkzeug für den Außendienst, aber auch für den Endkunden gedacht. Wer das Produktsortiment kennt, ist mit der Suche extrem effektiv.</p>
<p><img src="/images/aivde_02.png" alt="aivde"></p>
<p>Das Produktsortiment umfasst viele Halter, Hüllen, Stecker, Adapter und Kabel. Deshalb war die Such- und Filterfunktion extrem wichtig. Da die Daten allerdings in einem sehr veralteten und unstrukturierten System gehalten werden, musste die Datenbasis aufbereitet werden. Artikeldetails wie Kabellänge oder Farbe mussten auf Basis des Artikeltextes erzeugt werden. Hierfür habe ich ein internes Tool mit einer eigenen Scriptsprache entwickelt, mit der Muster definiert werden konnten, die dann auf die Artikeltexte angewandt wurden. Dies war notwendig, um die Suche und auch die Filter überhaupt möglich zu machen. Die Produktbilder stammen aus unterschiedlichsten Quellen und werden durch ein eigenes System zusammengetragen und aufbereitet.</p>
<p><img src="/images/aivde_01.png" alt="aivde"></p>
<p>Herausfordernd an diesem Projekt war die Zusammenführung und die Aufbereitung der vielen verschiedenen Datenquellen. Um eine einheitliche Suche bereitzustellen, mussten Daten auf unterschiedlichen Wegen (automatisiert) in ein einheitliches Format gebracht werden. Zudem war die Aktualität der Daten wichtig, so musste dieser Prozess so schnell und performant wie möglich gemacht werden. Die Aufbereitung der Artikeldaten war für mich persönlich sehr spannend.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/202
2010-12-04T23:00:00+01:00
2010-12-04T23:00:00+01:00
OSS Engineering (Advent2010): 2. Advent: Prototyp und Testen
<p>Florian ist diese Woche voll durchgestartet und hat mit einem Prototyp begonnen und mit diesem schon die Kernkomponenten implementiert. Der Prototyp war wichtig, um sicherzustellen dass die Wahl der verwendeten Komponenten (avahi und dbus) sinnvoll ist und wie sich die einzelnen Schichten voneinander abkoppeln lassen. Auf diese Basis kann nun aufgesetzt werden. Zuvor sollte aber auf jeden Fall sichergestellt werden, dass der Kern korrekt funktioniert und wir nicht auf einem Fehlerbehafteten Konzept aufbauen. Deshalb werden wir uns in diesem Beitrag um das Testen der einzelnen Komponenten kümmern.</p>
<p>Der erste Funktionstest besteht bei uns meist darin, die Klassen/Funktionen mit Dummy-Werten auf der Console oder mit simplem Testcode auszuprobieren. Ein paar <q>[pp](http://www.ruby-doc.org/core/files/lib/pp_rb.html)</q>"s genügen hier meist schon, um sicherzustellen dass die Funktion korrekt arbeitet bzw. die Kommunikation funktioniert. Da noch keinerlei UI besteht, muss man sich damit behelfen.</p>
<p>Ein Vorteil, wenn man standardisierte Schnittstellen bzw. Protokolle verwendet, besteht darin, dass es bereits viele Tools gibt, die mit dieser Schnittstelle umgehen können. Würden wir die Kommunikation mittels HTTP implementieren, könnten wir mit einem normalen Web-Browser testen, ob unsere Implementierung korrekt funktioniert. Ähnlich ist es bei DBus; wie Florian schon erwähnt hat, gibt es unzählige Tools, Bibliotheken und Programme, die darauf aufsetzen. Deshalb gibt es auch Tools wie d-feet, mit dem wir uns statt die 5-Seitige XML-Antwort durchzuackern, interaktiv mit dem Interface arbeiten können.</p>
<p><img src="/images/20101205_dbus_debug.png" alt="DBus testen"></p>
<p>Dies macht das Testen und Ausprobieren um ein vielfaches einfacher. So können wir die Events (Signals) über eine GUI mit beliebigen Parametern aufrufen und das Ergebnis validieren.</p>
<p>Ähnliches können wir auch mit Avahi machen. Mit avahi-discover können wir uns alle publizierten Services ansehen und somit gleich testen ob unser Service korrekt erkannt und publiziert wird.</p>
<p><img src="/images/20101205_avahi_debug.png" alt="Avahi testen"></p>
<p>Natürlich sind das keine automatisierten Tests, was wir aber in dieser explorativen Phase auch noch nicht benötigen. Ist der Kern der Anwendung dann weitgehend durchdacht und implementiert, können wir uns Gedanken über einige Testfälle machen. Diese können wir dann mit Hilfe eines Test-Frameworks wie <a href="http://rspec.info/">RSpec</a> exakt spezifizieren. Hiermit lassen sich Tests definieren, wofür die oben beschriebenen Tools nicht ausreichen. Bspw. 100 Dateien gleichzeitig an einen Client schicken oder lästige Randbedingungen wie eine Datei mit 0 Byte übertragen, Unicode-Zeichen im Dateinamen usw.</p>
<p>Leider ist diese Art von Tests für eine solche Anwendung etwas kniffliger. Manchmal lässt auch die verwendete Bibliothek es nicht zu, automatisierte Testfälle aufzustellen, die auf einem Rechner durchlaufen werden können. Dies ist dann der Fall wenn die Bibliothek (oder auch der eigene Programmcode) stark mit der Hardware oder der Umgebung verbunden ist. Wir hatten das Problem, dass wir DBus schlecht testen konnten, da die Ruby-Lib als Singelton implementiert ist.</p>
<p>Es gibt natürlich auch hier mehrere Möglichkeiten, dieses Problem zu umgehen, allerdings wollen wir es an dieser Stelle nicht zu weit treiben, da es sich hier nicht um eine hoch kritische Anwendung handelt. In der kommenden Woche werden wir uns mit den Themen Dateitransfer und Benutzerinteraktion (UI) beschäftigen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/201
2010-11-26T23:00:00+01:00
2010-11-26T23:00:00+01:00
Adventskalender: Adventskalender 2010
<p>Die letzten zwei Monate war es recht still hier. Das lag vermutlich daran, dass ich umgezogen bin und mich die Arbeit zur Zeit ziemlich einspannt. Doch an Weihnachten darf eines nicht fehlen: Genau, der Adventskalender von <a href="http://feitel.indeedgeek.de/">Florian</a> und mir. Auch dieses Jahr haben wir uns wieder viel Mühe gegeben (und werden/müssen es noch :) um euch wieder einen spannenden Exkurs in die OSS Welt zu geben.</p>
<p><img src="/images/20101127_adventskalender2010_header.png" alt="Adventskalender2010"></p>
<p>Wir werden dieses Mal ein Open Source Softwareprojekt von der Planung über die Umsetzung und Verbesserung bis hin zum Release und Lizenzierung beschreiben und anhand eines kleineren Programmierprojekts demonstrieren. Natürlich darf sich wieder jeder nach Herzenslust beteiligen. Jedes Wochenende gibt es von Florian und mir jeweils einen Beitrag. Eine Übersichtsseite gibt es hier:</p>
<p><a href="https://advent2010.aaron-fischer.net/">https://advent2010.aaron-fischer.net/</a> (<a href="https://advent2010.aaron-fischer.net/advent2010.xml">RSS-Feed</a>)</p>
<p>Wir wünschen euch viel Spaß beim Lesen und freuen uns über jeden Response! Der erste Artikel ist schon <a href="https://aaron-fischer.net/artikel/erster-advent-planung-und-design">online</a>.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/200
2010-11-24T23:00:00+01:00
2010-11-24T23:00:00+01:00
OSS Engineering (Advent2010): 1. Advent: Planung und Design
<p>Am ersten Advent-Wochenende werden wir uns mit der Planungsphase eines Open Source Projektes befassen. Warum es sich vom herkömmlichen Software Life Cycle unterscheidet und warum diese Phase genauso wichtig ist wie die Implementierung. Zudem werde ich die Features unseres Beispielprojekts vorstellen. Florian wird im Anschluss mehr auf das Design der Anwendung und Architekturüberlegungen eingehen.</p>
<p>Bevor wir starten noch einige obligatorische Dinge. Es wird zu jedem Advent (jeweils Samstag und Sonntag) einen Beitrag von mir und einen Beitrag von Florian geben. Jede Woche werden wir ein anderes Thema im Entwicklungsprozess von Open Source Software beschreiben und dies anhand eines realen Projektes demonstrieren. Eine Übersicht und auch die Links dazu finden sich in unserem virtuellen <a href="http://advent2010.aaron-fischer.net/">Software Life Circle Adventskranz</a>.</p>
<p>Jede Art von Beteiligung - sei es über einen Kommentar oder Anregung, die Beteiligung beim Source-Code oder ein schlichter Klick auf den Flattr-Button - zeigt uns, dass Euch dieses Projekt gefällt und wir uns die ganze Mühe und Vorarbeit nicht umsonst gemacht haben. Wir freuen uns über jeden Response!</p>
<p>Legen wir los!</p>
<p>Vermutlich hat jeder schon einmal über eine bestehende Softwarelösung geflucht oder sich gewünschte, für ein bestehendes Problem eine passende Software zu haben (oder zu finden). Als Software-Entwickler findet man sich noch viel öfter in dieser Situation - vermutlich deswegen, weil hier der Anspruch noch viel höher ist. Doch was soll man schon groß tun, wenn man vor einem solchen Problem steht? Als Software-Entwickler hat man das Privileg einer Alternative: Selbst den Compiler auszupacken und sich das Tool (oder das Feature) selbst programmieren. Doch warum seine kostbare Freizeit opfern, wenn man sowieso schon den ganzen Tag Software schreibt?</p>
<p>Hier gehen die Meinungen vermutlich weit auseinander. Ich finde, es hat etwas mit Kunst zu tun; dem Bedürfnis etwas aus dem Nichts zu kreieren das im besten Fall nicht nur mir, sondern auch anderen hilft. Für mich ist es eine Art innere Erfüllung, selbst die Macht zu haben, etwas gegen das Problem zu tun und eine elegante Lösung dafür zu schaffen.</p>
<p>Aus diesem Bedürfnis entstehen meist die besten Ideen. So gut wie jede Open Source Software beginnt mit einer simplen Idee, die ein konkretes und akutes Problem (besser) lösen soll. So war es auch bei der Idee für diesen Adventskalender: Das leidige Thema Filesharing scheint ein immer währendes Problem zu sein und zu bleiben. Unzählige Male habe ich in den vergangenen 15 Jahren versucht, eine Datei von einem Rechner zum anderen zu übertragen. Oft gab es irgend ein Hindernis. Es gibt zwar tausende Protokolle auf den unterschiedlichsten Schichten und noch mehr Clients dazu, doch irgendwie scheint es immer noch kein vernünftiges Tool zu geben, dass es erlaubt <strong>einfach und unkompliziert</strong> Dateien zwischen zwei Rechnern auszutauschen. Windows-Dateifreigabe/SMB? Umständlich, langsam, wie war noch gleich die IP? ... Jabber/ICQ/... hat noch nie zuverlässig und auf Anhieb ohne Port-Forwarding funktioniert, ... Als E-Mail schicken? ... Anhang zu groß. Ihr wisst vermutlich genau, wovon ich rede.</p>
<p>Es fehlt also <strong>ein simples Tool, das es ermöglicht ohne jegliche Konfiguration Dateien (sicher) unter zwei Rechnern austauscht</strong>. Und darum soll es in diesem Adventskalender gehen.</p>
<p>Die ersten Schritte bei der Softwareplanung sind meist Requirements Engineering und das Aufstellen von Use-Cases; sprich dem Kunden seine Wünsche zu entlocken und zu verstehen. Diesen Prozess können wir uns hier komplett sparen, denn wir wissen genau, was wir wollen und der Use-Case sind wir selbst. Trotzdem ist es gut, wenn die Features aufgeschrieben und von anderen Tools abgegrenzt werden, denn sonst verliert man sich schnell in Details und die Software wird nie fertig. (Die häufigste Ursache für nie veröffentlichter Software!)</p>
<p>Folgende Features soll die Anwendung mindestens am Ende enthalten, damit sie unser Problem löst:</p>
<ul>
<li>Eine Benutzerliste, die alle Rechner im lokalen Netz anzeigt, die das Programm geöffnet haben und Dateien empfangen wollen.</li>
<li>Einem ausgewählten Benutzer eine Datei beliebigen Formats schicken, die dieser annehmen oder ablehnen kann.</li>
<li>Verschlüsselung bzw. Signierung der übertragenen Daten mittels GPG.</li>
<li>Die Möglichkeit, Public-Keys von anderen in das lokale System zu importieren und zu verwalten.</li>
</ul>
<p>Die Anwendung soll nur im lokalen Netz und nur mit <q>Push</q> funktionieren (User sendet Datei statt User gibt Datei frei). Auch sollen Dinge wie DoS-Attacken o.ä. vorerst ignoriert werden, damit es nicht zu komplex wird. Im Vordergrund steht der einfache und sichere Austausch von Dateien.</p>
<p><img src="/images/20101126_filer_scribble.png" alt="Konzept"></p>
<p>Die Idee ist da, eine Vorstellung wie es am Ende auszusehen hat auch. Wie geht es weiter? Bei kommerziellen Software-Projekten beginnt hier das Schreiben vom Lasten- und Pflichtenheft und das Ausformulieren eines Projektauftrags. Danach oder parallel vermutlich auch Machbarkeitsstudien und erste Architekturüberlegungen. Wir werden gleich mit der Architektur und dem eigentlichen Design der Software beginnen. Würden wir direkt mit der Implementierung starten und darauf loshacken, müssten wir höchstwahrscheinlich diesen Schritt später nachholen. Viel Code müsste durch Refactoring und größere Umbau-Aktionen wieder gerade gebogen werden. Deshalb werden wir uns ein paar Gedanken zum Design der Anwendung machen, allerdings nicht alles bis ins kleinste Detail durchplanen. Das finale Design entsteht sowieso erst bei der eigentlichen Implementierung.</p>
<p>Weiter geht es <a href="http://feitel.indeedgeek.de/2010/11/1_advent_planung_und_design/">hier mit Florians Beitrag</a> zum Thema Design und Architektur-Planung sowie die Auswahl der geeigneten Tools, Techniken und Technologien. Wer sich bei der Planung beteiligen möchte, ist herzlich eingeladen Kommentare zu hinterlassen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/199
2010-09-10T22:00:00+02:00
2010-09-10T22:00:00+02:00
Gedanken: Mehr Power!
<p>In letzter Zeit höre ich immer häufiger, das man hier oder dort mehr Leistung, mehr Server, mehr Mitarbeiter oder mehr Software-Tools braucht. Natürlich ist es nicht schlecht wenn man von allem viel und reichlich hat, doch ich bin der Meinung, dass oftmals mit ein wenig Anstrengung mehr mit begrenzten Mitteln erreichen lässt.</p>
<p>Besonders bei der Programmierung lässt sich häufig ein Vielfaches an Rechenpower oder Speicherverbrauch einsparen. Ein simples Profiling der Anwendung zeigt schnell Engstellen auf, die sich mit ein paar einfachen Zeilen Code (Eine Variable an einer anderen Stelle definieren, Abbruchkriterien an den Anfang der Methode schreiben, Wiederverwendung von Speicher, unnötiges Kopieren, unnötige SQL-Joins rauswerfen etc.) eliminieren lassen.</p>
<p><img src="http://www.crazy-media.de/bilder/tv/hoer-mal-wer-da-haemmert/screen1.jpg" alt="Binford tools">
<em>(Bild: Walt Disney Studios - <q>Home Improvements</q>)</em></p>
<p>Im Serverbereich ist es natürlich das einfachste, neue Hardware zu bestellen, sobald der Server in der momentanen Konfiguration anfängt, an einer seiner Grenzen (Platz, Antwortzeit, Zugriffszahl) zu stoßen. Beim Aufsetzen eines Systems wurde anfangs vielleicht nicht daran gedacht, dass zwei Monate später 2000 User täglich einen unperformanten Service nutzen. Oft lohnt es sich, Feintuning an den Parametern vorzunehmen oder schlankere Software einzusetzen. Es braucht nicht immer ein J2EE Application Server in einer eigenen VM zu sein, um ein paar statische Webseiten auszuliefern.</p>
<p>Oft reicht es auch, die Zeiten zu nutzen, in denen die Hardware sowieso nichts zu tun hat. Vermutlich stört es nicht weiter, wenn nachts um 2 Uhr ein Skript 10.000 Bilder umkonvertiert und zuschneidet. Den Cloud-Ansatz von Google, Amazon und Co. finde ich auch nicht so verkehrt.</p>
<p>Auch bei Software-Tools bin ich immer wieder überrascht, wie viel Geld dafür ausgegeben wird und wie ineffizient diese dann genutzt werden. Ich bin ein großer Freund von Open Source und ich bin bis auf ein paar wenige Ausnahmen sehr zufrieden und auch produktiv damit.</p>
<p>In dem Buch <q>7 Habits of Highly Effective People</q> von Stephen Covey (Seite 172) ist eine Grafik beschrieben, die mir seit Jahren in solchen Situationen immer ins Gedächtnis springt:</p>
<p><img src="/images/20100913_manager_producer.png" alt="manager/producer"></p>
<p>Ich finde gerade das (aus bestehendem das Maximum herausholen) so spannend. Fast alles lässt sich mit etwas Verstand effizienter/performanter/einfacher gestalten. Diese Lektion habe ich noch einmal sehr deutlich gegen Ende meines Studiums erfahren: Ein komplexes Problem muss nicht komplex programmiert werden. Die einfachen Lösungen sind nicht nur leichter zu verstehen, sondern meistens auch performanter und schneller.</p>
<p>Allerdings erfordert diese Art der effizienten Ressourcenplanung auch Zeit, Geduld und Hirnschmalz. Wie seht ihr das? Ist es das wert oder ist die Arbeits(kraft|zeit) zu teuer, um sie mit solchen Dingen zu behelligen?</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/198
2010-08-28T22:00:00+02:00
2010-08-28T22:00:00+02:00
Programmiersprachen: Memory Management in Objective-C
<p>Die meisten <q>modernen</q> Programmiersprachen haben eine dynamische Speicherverwaltung, die von der VM oder dem Interpreter gesteuert wird. Manuelle Speicherreservierung (malloc) und Freigabe des nicht mehr verwendeten Speichers (free) sieht man heutzutage fast nur noch in historischen Projekten und an Stellen an denen der Speicher knapp ist.</p>
<p>In Objective-C wird dem Programmierer die Wahl gelassen, ob er den Speicher selbst verwalten oder es dem Garbage Collector überlassen will. Die Programme können sogar so geschrieben werden, dass sie beide Modis unterstützen um Abwärtskompatibel zu bleiben. (Bspw. um eine Library auf dem iPhone und in einer Desktop-Anwendung zu nutzen)</p>
<p>Die Einstellung wird in XCode mit einem Doppelklick auf das Build-Target erreicht:</p>
<p><img src="/images/20100829_cbjectivec_memory1.png" alt="Garbage Collector Schalter"></p>
<p>Wird der Garbage Collector ausgestellt, ist der Programmierer selbst für die Speicherallokation zuständig. Objective-C ähnelt etwas dem der Java VM. Jedes Objekt hält intern einen Referenzzähler (retain-count genannt), der angibt, wie viele andere Objekte dieses Objekt benötigen. Sinkt dieser Zähler auf 0, wird das Objekt aus dem Speicher entfernt.</p>
<p>Um den retain-counter zu inkrementieren, muss eine <strong>retain</strong>-Nachricht an das Objekt gesendet werden. Diese kann von allen Objekten entgegengenommen werden, die von NSObject abgeleitet wurden, also so gut wie alle. Zum Dekrementieren wird <strong>release</strong> verwendet. Ein kleines Beispiel:</p>
<pre><code class="c">NSArray *users = [[NSArray alloc] init]; // retain-count = 1
// Do some stuff
[users release] // retain-count = 0
</code></pre>
<p>Hierbei gibt es zwei wichtige Faustregeln (beide aus <a href="http://macdevcenter.com/pub/a/mac/excerpt/Cocoa_ch04/index.html">Memory Management in Cocoa</a> entnommen):</p>
<blockquote>
<p>You should never release an object that you have not retained or created.</p>
</blockquote>
<p>und</p>
<blockquote>
<p>Make sure that there are as many release or autorelease messages sent to objects as there are alloc, copy, mutableCopy, or retain messages sent. In other words, make sure that the code you write is balanced.</p>
</blockquote>
<p>Interessant wird es, sobald Objekte an andere Objekte gesendet werden. Hier hat das ursprüngliche Objekt keine Kontrolle mehr darüber, was mit dem Objekt passiert, und kann es deshalb auch nicht mehr sauber aus dem Speicher räumen.</p>
<p>Hier kommt der Autorelease Pool ins Spiel: In ihm lassen sich zur Speicherfreigabe vorgesehene Objekte vormerken, um dann an einer geeigneten Stelle die <q>release</q>-Nachrichten zu verschicken. Dies ist aufs Erste Mal nicht so ganz einleuchtend, deshalb wieder ein Beispiel:</p>
<pre><code class="c">NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSNumber *fourtytwo = [NSNumber numberWithFloat:42];
// Some stuff ...
[pool release];
</code></pre>
<p>In der ersten Zeile wird der Autorelease Pool erstellt. Er enthält alle Referenzen zu den Objekten, die aus dem Speicher entfernt werden können. Auch diejenigen, die von anderen Methoden erzeugt werden. In diesem Fall ist das das NSNumber-Objekt, das nicht über den Standard-Konstruktor <q>init</q> erzeugt wurde, also nicht in der aktuellen Methode sondern an einem anderen Ort. Da wir in unserer aktuellen Methode nicht wissen, ob das NSNumber-Objekt noch anderweitig verwendet wird (da wir es ja nicht direkt erstellt haben), können wir es auch nicht mit [fourtytwo release] bedenkenlos aus dem Ram löschen. Dafür ist der Autorelease Pool zuständig. Sobald das NSNumber-Objekt an keiner Stelle mehr verwendet wird, landet es in einem AutoreleasePool. Rufen wir dann [pool release] auf, wird an allen enthaltenen Objekten die <q>release</q>-Nachricht gesendet, welche einen release-count von 0 vorweisen.</p>
<p>Wenn der Speicher manuell verwaltet wird, treten meist ziemlich hässliche Fehler auf, die sehr schwer zu finden sind. Speicherlecks sind tückische Biester, die sporadisch auftauchen und nicht selten lange unentdeckt bleiben. Um dem entgegenzuwirken und das Debugging nach einem SegFault etwas einfacher zu machen, können diese beiden Optionen gesetzt werden (Doppelklick aufs Executable in XCode):</p>
<p><img src="/images/20100829_objectivec_memory2.png" alt="Speicherlecks besser finden"></p>
<p>Damit bricht das Programm kontrolliert ab und XCode springt direkt in die Zeile in der versucht wird auf ein Objekt zuzugreifen, dass zuvor schon aus dem Speicher entfernt wurde.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/197
2010-08-23T22:00:00+02:00
2010-08-23T22:00:00+02:00
Privates: Eigentlich schade
<p>Als ich mich vor ungefähr drei Jahren bei Facebook angemeldet hatte, war da noch nichts los. Die einzige deutsche Fan-Site war <q>Germany, the land of brezels and beer</q> und hatte ca. 150 Mitglieder. Ich hab dem ganzen auch keine große Bedeutung mehr geschenkt, da ein Social-Network ohne Freunde nicht besonders viel Spaß macht. Da war Twitter schon lustiger, vorausgesetzt man wollte immer genau wissen, was gerade so im Netz hyped. Doch im Großen und Ganzen war das für die meisten in Deutschland ziemlich irrelevant.</p>
<p>StudiVZ war der heiße Scheiß für alle, die keine andere Möglichkeit hatte sich im Web mitzuteilen. Man gruschelte sich gegenseitig und verlinkte sich auf Party-Bildchen. Diejenigen, die ein eigenes Blog hatten und einen RSS-FeedReader bedienen konnten, hatten zu der Zeit (schlimm jetzt schon <q>zu der Zeit</q> zu schreiben!) eine tolle Möglichkeit schnell an selektierte Informationen zu gelangen und sich der Welt zitierfähig mitzuteilen.</p>
<p>Heftige Diskussionen wurden in Blogs geführt, Streitereien ausgetragen, Geheimnisse ausgeplaudert und zu jedem Problem gab es schon jemanden, der die Lösung in seinem Blog veröffentlichte. Doch seit circa einem Jahr scheint sich wieder ein Medienwechsel durch das Web zu ziehen. Und zum ersten Mal gefällt mir nicht, was ich erlebe.</p>
<p>Betrachte ich meinen Feedreader, rauschen zwar noch in gewohnter Regelmäßigkeit Artikel von Lifehacker, io9, Spreeblick und Nerdcore durch, doch die Blogs meiner Freunde sind seit Monaten stillgelegt. Das Blog meiner Freundin ist schon seit einem halben Jahr tot, der letzte Twitter-Post ist von gestern. Sterben gerade die kleinen Blogs weg, ohne dass es uns bewusst wird? Ist RSS/Atom Käse von Vorgestern, für das seit Facebook-Pinwand und Twitter-Timeline kein Bedarf mehr besteht?</p>
<p>Woran liegt es, dass keiner mehr Lust hat zu bloggen? Liegt es an den ganzen Social-Networks, dass niemand mehr die Muse hat, sich für einen Blogartikel Mühe zu geben? Ich kann nur für mich sprechen: Seit Twitter und Facebook hat sich für mich das Verständnis für einen Blogartikel stark verändert. Ein Tweet muss nicht originell oder fehlerfrei sein. Selbst wenn keiner es versteht, in zwei Tagen ist der Eintrag schon unterhalb des Browserfensters verschwunden und keiner wird ihn vermissen. Bei einem Blog-Artikel sieht das nun ganz anders aus: Einmal geschrieben, gleich von Google erfasst und für alle Ewigkeit im Netz. Über die Kommentare (vorausgesetzt man hat willige Kundschaft) wird auf jeden Fehler hingewiesen, der Chef liest mit und der Link dazu landet in der Bookmarksammlung.</p>
<p>Diese Vorstellung schreckt ab. Auch mit diesem Blog-Beitrag habe ich wieder mal ewig gewartet. In den vergangenen Monaten ist so viel passiert, über das ich schreiben wollte, doch nie fand ich es würdig genug. Warum auch immer. Diesem Trend will ich in Zukunft wieder gegensteuern und gelobe Besserung.</p>
<p>(Die Datumsangabe ist vom letzten veröffentlichten Blogbeitrag)
Ganz speziell will ich Bea (~2009), Klaus (Februar 2010, ich bin enttäuscht!), Florian (Mai 2010), Till (Februar 2010, dir sei verziehen), Ruben (Dezember 2009), Kai (Juni 2010, vorbildlich!), Markus (Juni 2010), Daniel (August 2009), Andreas (August 2009), Thomas (November 2009) und alle anderen vergessenen Freunde dazu anregen, mal den Spam aus den Kommentaren zu löschen und mal wieder zu bloggen. Ich würde mich sehr freuen, wenn wir -- ja wir, die die Blogosphäre erschaffen haben -- die Blogs wieder aufleben lassen und Texte verfassen, die länger als 144 Zeichen lang sind.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/196
2010-07-13T22:00:00+02:00
2010-07-13T22:00:00+02:00
Projekte aus dem Studium: Done!
<p>Es ist vollbracht.</p>
<p><img src="/images/20100714_thesis2.png" alt="85 Seiten konkatenierte Erkenntnisse."></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/195
2010-06-22T22:00:00+02:00
2010-06-22T22:00:00+02:00
Netzkultur: Plaintext Passwords for the shame!
<p>Vor Kurzem habe ich all meine Passwörter in eine neue Passwortverwaltung umgezogen. Dabei habe ich mir den Spaß gemacht, die <q>Passwort vergessen</q>-Funktion vieler Websites und anderer Systeme auszuprobieren, um danach das Passwort auf ein neues Zufallspasswort zu setzen. Was mir allerdings da widerfahren ist hat mich schon geschockt!</p>
<p>Folgende bekannte Seiten speichern die Passwörter sämtlicher Nutzer <strong>im Klartext</strong>:</p>
<ul>
<li>backpackit.com</li>
<li>sipgate.de (WAT?)</li>
<li>selfhtml.de</li>
<li>symlink.ch</li>
<li>musicbrainz.org</li>
<li>... und mehrere kleinere Webseiten</li>
</ul>
<p>Das hat mich schon ziemlich erschreckt. Ich dachte eigentlich, dass Größen wie musicbrainz, Sipgate oder 37signals ein kleines bisschen auf Datenschutz wert legen. Da hilft dann nur abmelden oder regelmäßig das Passwort ändern.</p>
<p>Apropos ändern: Einige Seiten baten nicht mal die Möglichkeit an, sein Passwort zu ändern. Die Löschung eines Accounts ist auch selten vorgesehen. Schon mal versucht in einem <strong>Wordpress</strong> oder einem <strong>PHPBB</strong> einen Benutzer zu löschen? Diese Funktion ist einfach nicht vorgesehen.</p>
<p>Und wenn man dann den Betreiber anschreibt, er solle doch bitte den Account incl. Daten löschen, bekommt man in den meisten Fällen die Antwort: <q>Das geht leider nicht, melde dich doch einfach nicht mehr an, die Daten sind hier sicher</q> .... alles Klar.</p>
<p><strong>Nachtrag 2012-08-15</strong>: 1und1 scheint hier <a href="http://blog.tim-rogers.co.uk/posts/1and1-ask-for-passwords-over-the-phone">mal wieder</a> total <a href="https://aaron-fischer.net/blog/geocaching-mit-1und1">versagt</a> zu haben. Wundert mich eigentlich nicht mehr.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/193
2010-06-08T22:00:00+02:00
2010-06-08T22:00:00+02:00
Projekte: The most awesome colorscheme for vim
<p>Ich meckere ja gerne über die hässlichen Farbschemas für Vim. Ich hab schon etliche ausprobiert und ein paar davon hatten mir sogar gefallen. Leider gab es für mich noch nicht <q>das perfekte</q> Farbschema, das überall gut aussah. Aus diesem Grund hab ich beschlossen, selbst eines anzufertigen.</p>
<p><img src="/images/20100609_syntax_highlighting_fu_2_thumb.png" alt="The awesome colorscheme for vim"></p>
<p>Das Farbschema ist noch nicht ganz fertig, und an einigen Stellen muss noch einmal nachgebessert werden, doch ist es -- in meinen Augen -- das brauchbarste, das es in der vim.org Scripts Grabbelkiste gibt. Ich habe mich bemüht, eine dezente und dennoch klare Farbpalette zu erstellen. Verbesserungen kommen bestimmt in den nächsten Wochen, wenn ich mit anderen Programmiersprachen gearbeitet habe.</p>
<p><a href="http://www.vim.org/scripts/script.php?script_id=3117">Download von vim.org</a> (<a href="http://images.datenhalter.de/syntax_highlighting_fu_1.png">Screenshot 1</a>, <a href="http://images.datenhalter.de/syntax_highlighting_fu_2.png">Screenshot 2</a>)</p>
<p><strong>Edit (10.06.2010):</strong> Danke für die Beiträge! Habe eben eine neue Version mit den Änderungen hochgeladen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/194
2010-06-08T22:00:00+02:00
2010-06-08T22:00:00+02:00
Projekte aus dem Studium: Syscalls aus Ruby
<p>Für unsere Thesis hatten wir uns gestern nochmal etwas mit dem FFI (Foreign Function Interface) unseres Interpreters beschäftigt. Es geht darum, Funktionsaufrufe aus anderen Programmiersprachen im eigenen Interpreter aufzurufen. Da wir Ruby als Hostsprache gewählt haben, wollten wir auch nativen Ruby-Code in unserer Skriptsprache verwenden.</p>
<p>Das Wort, welches einen TCP-Socket öffnet, sieht so aus: (Wort deswegen, da es sich um eine <a href="http://www.concatenative.org/wiki/view/Concatenative%20language">konkatenative Programmiersprache</a> handelt. Alles was nach dem <q>with</q> kommt, ist nativer Ruby-Code. Mehr dazu, nachdem die Thesis abgegeben wurde :)</p>
<pre><code class="ruby">:open-tcp-socket $host $port => { $host $port { $id <<native>> } <<socket>> } with
require "socket"
socket = TCPSocket.new(bindings.get("$host"), bindings.get("$port").to_i)
bindings.bind("$id", store_native(socket))
.
</code></pre>
<p>Wir haben uns natürlich andere Implementierungen angesehen. Factor beispielsweise hat sein FFI <a href="http://docs.factorcode.org/content/vocab-unix.ffi.html">verdammt elegant gelöst</a>. Dadurch, dass Factor in C++ geschrieben ist, ist hier der Weg zum Betriebssystem nicht so weit entfernt wie bei Ruby. Die Definition von der Systemfunktion <q>rename</q> sieht bei Factor so aus:</p>
<pre><code class="ruby">USING: alien.c-types alien.syntax ;
IN: unix.ffi
LIBRARY: libc FUNCTION: int rename
( c-string from, c-string to ) ;
</code></pre>
<p>So etwas wollten wir natürlich auch. Glücklicherweise gibt es ein total undokumentiertes Feature in Ruby, welches dem gewillten Programmierer erlaubt, Funktionen aus Shared Libs aufzurufen. Es nennt sich Ruby/DL. Dieses Feature lässt sich nun nutzen, um einen ähnlichen Komfort wie bei Factor zu erzeugen:</p>
<pre><code class="ruby">require "dl"
class FFI
def initialize
@libc = DL.dlopen("libc.so.6")
@types = {String => "S", Fixnum => "I", Float => "F", nil => "0"}
end
def syscall(function, return_type, *parameters)
params = [@types[return_type]] + parameters.map {|param| @types[param.class]}
fun = @libc[function, params.to_s]
fun.call(*parameters)
end
end
</code></pre>
<p>Die Klasse macht im Prinzip nur das Type-Mapping, ruft die Funktion auf und gibt das Ergebnis zurück. Somit braucht es nur noch ein Wort (vorausgesetzt, man weiß welche Parameter die Funktion benötigt), um das FFI anzusprechen.</p>
<pre><code class="ruby">:syscall #function #return_type { @params } => { @result } with
FFI.new.syscall(bindings.get("$function"), bindings.get("$return_type"), bindings.get("@params"))
.
:rename #from #to => rename <<integer>> { { #from <<string>> } { #to <<strong>> } } | syscall .
</code></pre>
<p>Das ganze Vorgehen ist natürlich sehr vereinfacht. Dinge wie Structs, Arrays oder Pointer müssen natürlich speziell behandelt werden. Auch verabschiedet sich Ruby regelmäßig mit einem SegFault, wenn man die Funktion mit <q>falschen</q> Parametern aufruft.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/192
2010-05-27T22:00:00+02:00
2010-05-27T22:00:00+02:00
Netzkultur: Geocaching mit 1und1
<p>Meine Freundin schickte mir heute einen Link zu einem Artikel auf <a href="http://geocaching-blog.de/?p=910">geocaching-blog.de</a>. Anscheinend plant also 1und1 - der große Webhoster - auch einen auf Social-Media-Flickr-Twittr-Facebook-Freund zu machen. Was genau soll noch nicht verraten werden, zumindest ist die <a href="http://www.facebook.com/group.php?gid=126085924078004&v=info">Facebook-Gruppe</a> komplett leer und mit ziemlich viel null-Inhalt gefüllt.</p>
<p>Da wir bei <a href="http://www.geocaching.com/seek/nearest.aspx?ul=aaronmueller">solchen Sachen</a> gerne mitmachen, habe ich natürlich angenommen, dass dies teil des Spieles ist. Also schnell <a href="http://www.google.de/search?num=50&hl=de&safe=off&client=firefox-a&hs=8hc&rls=com.ubuntu%3Ade%3Aofficial&q=geocaching+mit+1und1+%22Michael+Deutsch%22&aq=f&aqi=&aql=&oq=&gs_rfai=">google angeworfen</a> und <a href="http://www.na-media.de/www/geocaching.1und1.de/news">die</a> <a href="http://twitter.com/1und1Geocaching">beiden</a> Treffer angeklickt. Das eine ist ein etwas eigenartiger Twitter Account, in dem dasselbe wie in der Facebook-Gruppe steht. Mit der Ausnahme, dass hier jeder zweite Satz in einer neuen Twitter-Nachricht steht (wie soll denn auch der ganze Vorgabe-Text in 140 Zeilen passen, das geht doch gar nicht!).</p>
<p>Der zweite Link führt zu einer Seite die eigentlich genau dem entspricht, nach dem ich gesucht hatte. Nur die URL ist etwas eigenartig. Die URL auf geocaching.1und1.de zu ändern führt ins Leere, also schnell dort <a href="http://www.na-media.de/www/geocaching.1und1.de/anmeldung">anmelden</a>. Das Formular sagt <q>Erfolgreich abgesendet</q> und leert sich danach. Komisches Verhalten, aber das soll mir ja egal sein. Als nach ~10 Minuten immer noch keine E-Mail mit den Koordinaten angekommen war, klickte ich einfach mal ohne Daten einzugeben auf den Absenden-Knopf des Formulars. <q>Das Formular wurde erfolgreich verwendet</q> ... okay, scheint wohl entweder ziemlich beschissen programmiert zu sein oder irgend was anderes ist defekt. Weil das Formular nicht tut, hatte ich an die unten angegebene Adresse eine E-Mail geschickt, mit der Bitte das Formular zu fixen oder mich so anzumelden. 5 Sekunden später sagt mir ein schlund.de Mailserver, das die Adresse <q>permanent fatal errors</q> hat. Super klasse.</p>
<p>Schon ziemlich angepisst geh ich auf <a href="http://www.na-media.de/">na-media.de</a>, die diesen viralen Versuch, hip zu sein, verbockt haben. Und schon beim Klick auf <a href="http://www.na-media.de/business_features.php">Leistungen</a> (<a href="http://webcache.googleusercontent.com/search?q=cache:TIw8jbkQyiQJ:www.na-media.de/www/geocaching.1und1.de/news+geocaching+mit+1und1+%22Michael+Deutsch%22&cd=2&hl=de&ct=clnk&gl=de&client=firefox-a">Cache</a>) springen einem graue Schrift auf grauem Hintergrund und Powerpoint-Bilder mit eingeschalteter Rechtschreibkorrektur entgegen. Die Shift-Taste scheint auch zu hängen.</p>
<p>Wie schlecht ist dass denn bitte? Unprofessioneller kann man es doch gar nicht mehr machen. Werde mich jetzt mal an <a href="http://blog.1und1.de/2009/12/25/marcell-davis-leiter-fuer-kundenzufriedenheit-2/">Marcell D"Avis</a> wenden, vielleicht hat der ja ne Ahnung was das soll.</p>
<p><strong>Edit (28.05.2010):</strong> Marcell D`Avis hab ich noch nicht erreicht aber ein Mitarbeiter von der SocialSoftwareSolutions GmbH (vermutlich die Frau Rentzsch) hat mich aufgeklärt: Die Aktion sollte erst nächsten Dienstag starten und die Webseite war wohl aus Versehen schon online. Alle Spuren wurden beseitigt und auch der Webmaster hat seinen Fuß von der Shift-Taste genommen. Sehr agil, das muss man ihnen lassen!</p>
<p><strong>Edit (30.05.2010):</strong> Also entweder will sich 1und1 von dieser verpatzten Aktion distanzieren oder sie wussten echt nicht was da vor sich geht. <a href="http://geocaching-blog.de/?p=914">Jens Stelmaszyk</a> hat ein <a href="http://twitter.com/1und1/status/14904778586">Statement vom Social Media Team von 1und1</a> auf seinem Twitter-Account gefunden. Näheres vermutlich erst Montag.</p>
<p><strong>Edit (02.06.2010):</strong> Die Aktion scheint wohl doch geplatzt zu sein. Schade eigentlich, fand die Idee an sich nicht schlecht.</p>
<p><strong>Edit (16.06.2010):</strong> Die Webseite ist ja tatsächlich wieder online. Also wer noch mitmachen will, viel Spaß.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/191
2010-05-15T22:00:00+02:00
2010-05-15T22:00:00+02:00
Browser & Betriebssysteme: Mobile Version
<p>Da ich mir vor kurzem ein Android Smartphone (Motorola Milestone) zugelegt habe, war natürlich das erste was ich tun wollte, mit der Hardware spielen. Leider hab ich aus Zeitmangel nicht mehr als eine kleine Test-Applikation zustande gebracht. Das wird sich hoffentlich in den nächsten Wochen ändern.</p>
<p>Beim Herumspielen und Surfen mit den Android ist mir aufgefallen, dass so gut wie niemand seine Webseite für kleine Geräte optimiert. Manche Websites lassen sich schlicht mehr nicht bedienen. Dies ist mir schon mit dem eeePC aufgefallen und ich war ständig mit der horizontalen Scroll-Leiste beschäftigt. Es gibt aber auch Ausnahmen: alistapart.com beispielsweise lässt sich wunderbar bedienen!</p>
<p><img src="/images/20100516_mobile_version.png" alt="Smartphone Version"></p>
<p>Meine Webseite wird zwar korrekt auf dem Android dargestellt, allerdings ist sie dort total überladen. Und da ich auch zu den coolen Jungs gehören will, hab ich meine Seite auch dementsprechend optimiert. Surft man meine Seite mit einem mobilen Gerät an, gibt es nicht nur ein alternatives Stylesheet, sondern auch eine auf das Minimum abgespeckte Version des Inhalts dieser Seite. Maximaler Informationsgehalt für ein Minimum an Bytes sozusagen :-)</p>
<p>Da ich es leider nur auf einem einzigen Gerät getestet habe, kann es natürlich gut sein, dass es auf anderen Displays mit anderen Browsern und anderen Smartphones anders aussieht. Ich würde mich bei Darstellungsproblemen über eine kurze E-Mail sehr freuen - am besten mit Bild.</p>
<p>Um die Webseite zu besuchen einfach auf aaron-fischer.net gehen oder diesen QR-Code scannen.</p>
<p><img src="http://qrcode.kaywa.com/img.php?s=8&d=http%3A%2F%2Faaron-fischer.net%2F" alt="QR-Code"></p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/190
2010-04-29T22:00:00+02:00
2010-04-29T22:00:00+02:00
Technologie: Das Backup-Problem
<p>Über das Thema Backup wollte ich schon lange einmal schreiben - jetzt ist denke ich ein guter Zeitpunkt dafür. Ich war lange auf der Suche nach der perfekten Backup-Strategie und seit ein paar Jahren habe ich für mich ein System gefunden, mit dem ich recht gut zurechtkomme. Dieses will ich nun einmal beschreiben.</p>
<p>Da ich viel mit dem Notebook unterwegs bin, habe ich zwangsläufig mehrere Orte, an denen ich meine Daten brauche - und dementsprechend auch miteinander synchronisieren muss, da die Datenbestände zwangsläufig divergieren. Ich habe mich für eine Kombination aus <a href="http://de.wikipedia.org/wiki/Network_File_System">NFS</a>, <a href="http://samba.anu.edu.au/rsync/">rsync</a> und <a href="http://www.cis.upenn.edu/%7Ebcpierce/unison/">unison</a> entschieden. Zu Hause habe ich einen alten PC mit drei Festplatten ausgestattet und mir mit <a href="http://freenas.org/freenas">FreeNas</a> ein recht stabiles <a href="http://de.wikipedia.org/wiki/Network_Attached_Storage">NAS</a>/<a href="http://de.wikipedia.org/wiki/RAID#Software-RAID">RAID</a>-System aufgesetzt. Auf diese Daten greife ich lokal über NFS-Freigaben zu und arbeite direkt darauf. Das Notebook synchronisiere ich mit unison ab und an damit, so dass ich auch unterwegs alle Daten habe.</p>
<p>Natürlich kann ich nicht alles synchronisieren und ein dreifach-Backup anlegen.<br>
Allein meine Medien (Bilder, Videos, eBooks, MP3, ...) die sich über die
Jahr(zehnte?) angesammelt haben, nehmen ca. 500GB ein. Diese werden über das <a href="http://de.wikipedia.org/wiki/RAID#RAID_1:_Mirroring_.E2.80.93_Spiegelung">RAID1</a> auf zwei Platten gespiegelt. Der Rest (Code, Texte, Studium, Konzepte, Projekte, ...) sichere ich zusätzlich jede Nacht mit rsync auf meinem vServer. Für was hat man denn sonst DSL? :) Dies hat den angenehmen Vorteil, dass ich mein Datenbestand auch <q>remote</q> abgleichen kann, falls ich mal ein paar Tage nicht zu Hause bin und mir nicht den aktuellen Stand geholt habe.</p>
<p>Dieses System funktioniert nun schon seit mehreren Jahren, auch nach mehreren Hardware-Defekts und Umzügen. Was ich allerdings bis gestern komplett außer Acht gelassen hatte, waren die Daten, die ausschließlich auf meinem vServer liegen - also auch dieser Text hier. Dies wurde mir bewusst, als mein kompletter Hoster über mehrere Stunden nicht erreichbar war. In solchen Situationen fängt dann die Panik an: <q>Was ist da los? Server abgeschmiert oder nur ein Netzwerkproblem?</q> Also sichere ich ab jetzt auch täglich die wichtigsten Verzeichnisse zurück nach Hause auf mein NAS.</p>
<p>Was das Thema Backup angeht hat (hoffentlich) jeder sein eigenes System. Die meisten machen gar nichts und fangen alle paar Jahre von Neuem an oder haben sowieso alles online in Facebook, GitHub und Co. Andere machen sporadisch ein Backup auf einer USB-Festplatte. Zu hoffen, dass sowieso nichts passieren wird ist leider eine ganz schlechte Strategie.</p>
<p>Warum ich eigentlich diesen Beitrag schreiben wollte war die Tatsache, dass ich neulich ein paar Backup-Dateien von vor sieben Jahren fand, mit denen ich nichts mehr anfangen konnte. Ich wusste schlicht nicht, welches Tool zu dieser Datei passt. Nach langem Suchen fand ich dann eine total veraltete Shareware-Version der Backup-Software (Support seit Windows 2000 eingestellt), die ich damals verwendet hatte. Umständlich mit Wine habe ich es dann unter Linux irgendwie zum Laufen gebracht und konnte tatsächlich noch ein paar Schätze retten.</p>
<p><img src="/images/20100430_old_backup.png" alt="abackup, ein Win98-Tool für Backups"></p>
<p>Auch fand ich noch eine CSV-Datei mit einer Menge Bookmarks. Solche Formate sind zwar nicht sehr elegant oder effizient, aber robust gegen die Zeit; das Backup der Bookmarks war knapp 10 Jahre alt und trotzdem konnte ich mit drei Zeilen Ruby eine simple HTML-Seite generieren, mit der ich sie dann in mein delicious-Account importieren konnte.</p>
<p>Es lohnt sich also immer, offene und zugängliche Formate für ein Backup zu verwenden (schon mal das Passwort für ein längst verschollenes Zip-Archiv aus dem Jahre 2000 erraten müssen?). Eigentlich lohnt es sich überall, nicht nur für das Backup. Programme, Betriebssysteme, Codierungen und Versionen ändern sich zwangsläufig mit der Zeit, und wenn es nur die Entscheidung ist, ein schickes MacBook zu kaufen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/189
2010-01-14T23:00:00+01:00
2010-01-14T23:00:00+01:00
Privates: Und wieder ein Jahr rum
<p>Ich habe lange nichts mehr von mir hören lassen. Dies hatte mehrere Gründe: zum einen war ich die letzten Wochen und Monate ziemlich mit Prüfungen beschäftigt und zum anderen war ich etwas geknickt vom Adventskalender und der mangelnden Beteiligung. Twitter und Facebook waren allerdings auch nicht unschuldig an der fehlenden Lust etwas im Blog zu veröffentlichen. (Beim vorletzten Chaos Treffen hatten wir das Thema ja <a href="http://www.cthn.de/Zukunft_von_Mikroblogging">schon zu Genüge erörtert</a>) Trotzdem finde ich das Bloggen weiterhin spannend und werde es nicht sein lassen!</p>
<p>Aber nun zum Jahresrückblick: Das letzte Jahr war ein sehr spannendes und ereignisreiches Jahr, in dem ich sehr viel erlebt, gelernt und getan habe. Von ein paar Ereignissen möchte ich hier berichten - nicht nur für euch, sondern auch für mich als Reflexion.</p>
<p>Am Anfang veranstaltete ich einen etwas aufwändigeren Programmierwettbewerb (Chat-Client) und bewarb mich bei IBM auf eine Praxissemesterstelle. Dort wurde ich genommen und fuhr die darauf folgenden 7 Monate jeden Tag zusammen mit Klaus von Heilbronn nach Stuttgart, um an einem ziemlich aufwändigen Projekt zu arbeiten. Auf der Autobahn hörten wir so ziemlich jedes Hörbuch und jeden Podcast von Frankenstein bis ChaosRadio. Es war eine ganz neue Erfahrung für mich, in so einem großen Unternehmen zu arbeiten und ich hab einiges über Projektmanagement, Konfliktbewältigung und den Umgang mit (schwierigen) Kunden gelernt. Gelesen habe ich in der Zeit allerdings verhältnismäßig wenig.</p>
<p>Im Sommer habe ich zusammen mit Bea einige Caches gehoben (GeoCaching, ~35 Stück dieses Jahr) und auch versucht selbst einen zu legen. Leider gestaltete sich dies als äußerst nervenaufreibend und zeitintensiv, so dass dieser immer noch nicht an seinem Platz ist. Zusammen mit Florian haben wir ein paar Nächte mit <a href="http://www.wesnoth.org/">Battle Of Wesnoth</a> zugebracht, von denen bis heute keiner gerne redet :)</p>
<p>Zum Ausklang des Sommers gab es Urlaub am Bodensee mit Zelt und Campingkocher. An einem ziemlich heißen Spätsommertag Blut zu spenden ist keine gute Idee, das weiß ich jetzt auch :) Dafür gabs Freikarten für TripsDrill. Im September (um genau zu seine am 24.) hörte ich mit dem rauchen auf und hab die letzten Sommertage mit einem 10 km Lauf (ebm Papst Marathon) und Slacklining im Park genossen.</p>
<p>Im Oktober dann legte ich mir ein neues Notebook (Lenovo S12) zu und Florian half mir mit der Gentoo-Installation. Zu dieser Zeit begann mein letztes Fachsemester mit zwei schweren Mathevorlesungen und einem aufwändigen Theoriefach. Im November dann machte ich beim NaNoWriMo mit, was ich allerdings nicht ganz geschafft hatte. Bei zwei dritteln (~35.000 Wörtern) bin ich ausgestiegen, das wird dieses Jahr besser!</p>
<p>Im Dezember habe ich zusammen mit Florian den Adventskalender veranstaltet, der einiges an Zeit in Anspruch genommen hatte. Ich probierte verschiedene Plattformen wie die Nintendo Wii oder Googles Android aus, um das Spiel aus dem Adventskalender zu portieren, hatte aber keine Zeit mehr darüber zu schreiben, denn Weihnachten stand vor der Türe. Mein kleiner Bruder verbrachte Weihnachten und die restlichen Feiertage im Krankenhaus, weil er sich ein paar Sehnen/Nerven am Finger durchgeschnitten hatte. Für einen Programmierer/Grafiker/PC-Mensch gibt es glaube ich nichts Schlimmeres. Nach den Festtagen und der ganzen Aufregung war ich dann auf dem 26c3 in Berlin.</p>
<p>Natürlich gab es noch einiges mehr zu erzählen, doch ich denke das genügt. Als Ersatz noch ein paar Statistiken: Im Jahr 2009 hab ich 608 Bookmarks in <a href="http://delicious.com/aaron_mueller">delicious.com</a> gespeichert. Anfang des Jahres hatte ich eine <a href="http://imgriff.com/2008/01/13/neue-ideen-mit-der-100er-liste/">Liste mit 100 Wünschen</a> aufgeschrieben (gar nicht so einfach, probiert es mal aus!) von denen ich nun 42 streichen konnte! Eine gute Bilanz wie ich finde.</p>
<p>In diesem Sinne starte ich auch ins Jahr 2010: Es wird sich wieder viel verändern, viel geschehen und vieles ganz anders kommen wie man es sich zuvor ausgemalt hat.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/188
2009-12-27T23:00:00+01:00
2009-12-27T23:00:00+01:00
Privates: 26C3 & das erste mal Berlin
<p>Zusammen mit <a href="http://feitel.indeedgeek.de/">Florian</a>, Martin Sigloch und <a href="http://beatrice-fischer.de/">Bea</a> bin ich dieses Jahr zum <a href="http://ccc.de/">CCC</a> Kongress (<a href="http://26c3.de/">26c3</a>, <a href="http://www.heise.de/newsticker/meldung/26C3-Chaos-Computer-Club-erkundet-unbekannte-digitale-Welten-875662.html">heise</a>, <a href="http://www.youtube.com/watch?v=2zwEX2CSwJM&feature=related">tagesthemen</a>) nach Berlin gefahren. Zum Glück waren <a href="http://reutterblog.blogspot.com/">Michael</a> und <a href="http://twitter.com/TabTwo">Jason</a> schon am Abend davor da und haben uns Bändchen gekauft, denn schon am ersten Tag gegen Mittag war alles ausverkauft. Ohne sie hätten wir wieder heimfahren können. Nochmals vielen Dank dafür!</p>
<p><img src="/images/20091228_ccc_kongress.png" alt="Flags"></p>
<p>Und auch tolle Plätze hatten wir. In Sofas im ersten Stock mit Blick auf den Fernsehturm Richtung Alexanderplatz. Vor unseren Füßen gab es jeden Abend eine <a href="http://events.ccc.de/congress/2009/Fahrplan/events/3696.en.html">Tesla Show</a> die jeden Tag spektakulärer wurde. Anfangs waren es nur ein paar Blitze, ein Tag später spielte die Teslaspule schon Musik (<a href="http://www.youtube.com/watch?v=gtqwIQAlwjo">YouTube</a>). Das WLan war am ersten Tag erwartungsgemäß schlecht, nachdem sich aber Jason und Florian mit Switchen ausgestattet hatten, konnten wir entspannt über Kabel ins Netz. An dieser Stelle nochmal Danke an Florian, der auf seinem Server einen VPN-Tunnel eingerichtet hat, um nicht ungeschützt ins Netz zu müssen :-)</p>
<p><img src="/images/20091230_ccc_martin.png" alt="Martin"></p>
<p>Es gab viel zu sehen und zu lernen. In den ersten beiden Tagen waren wir in einigen Vorträgen, die restlichen Tage haben wir die Streams gemütlich auf dem Sofa verfolgt. Dies war auch besser so, denn bei den beliebteren Vorträgen und Veranstaltungen musste man sich schon eine halbe Stunde zuvor anstellen um einen Stehplatz zu bekommen. Die Streams waren dieses mal wieder sehr professionell gemacht, auch wenn sie zu Spitzenzeiten ein paar mal ruckelten.</p>
<p><img src="/images/20100103_ccc_room_full.jpg" alt="Room full"></p>
<p>Interessant fand ich auch, dass man auf dem Kongress viele bekannte Gesichter gesehen hat, die man normalerweise nur von berühmten Blogs, Podcasts oder dem Fernsehen kennt.</p>
<p>Geschlafen haben wir in einer ziemlich billigen Absteige im Osten von Berlin, doch das war uns egal, denn alles war besser als in einer Turnhalle oder auf einem Stuhl zu schlafen. Berlin an sich ist eine echt tolle Stadt! Leider haben wir nicht all zu viel davon angeschaut, da wir die meiste Zeit auf dem Kongress waren.</p>
<p><img src="/images/20100103_ccc_bea.jpg" alt="Bea in Berlin"></p>
<p>Vom Alexanderplatz war ich etwas enttäuscht, den hab ich mich etwas spektakulärer vorgestellt. Aber allgemein würde ich fast behaupten, dass die Menschen in Berlin einen Tick menschlicher, sozialer, hilfsbereiter und freundlicher sind als bei uns im Schwabenländle. Ich werde auf jeden Fall bei der nächsten Gelegenheit wieder nach Berlin gehen!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/187
2009-12-23T23:00:00+01:00
2009-12-23T23:00:00+01:00
Open Source (Advent2009): Tag 24: Ausblick, wie gehts weiter?
<p>Geschafft! Wir haben ein kleines Spiel von Anfang bis zum Ende begleitet. Wirklich bis zum Ende? Das ist jetzt die Frage. Gibt es ein <q>Ende</q> bei einem Open Source Projekt? Wann hört man auf mit Entwickeln? Was passiert, wenn der Hauptentwickler die Lust verliert? Haben wir etwas vergessen? Das werden wir jetzt klären.</p>
<p>Was passiert eigentlich, wenn wir ein Projekt für <q>fertig</q> erklären? Ein paar User laden sich die Software oder das Spiel herunter, benutzen es einige Zeit und wenn sie es für gut empfinden, empfehlen sie es weiter. Das Programm verbreitet sich. Und mit zunehmender Verbreitung steigt auch die Anzahl der Testkandidaten, die das Programm nutzen und testen. Jetzt liegt es in der Macht der User, ob das Projekt groß und berühmt wird oder nicht. Wenn es viele verwenden, tauchen zwangsläufig auch viele Fehler und Wüsche an das Programm auf. Fließen diese Fehler und Wünsche zurück an den/die Entwickler, können sie sich dem Problem annehmen und das Feature einbauen bzw. den Fehler fixen. Fließt kein Feedback zurück, bleibt die Software Fehlerbehaftet und schlecht getestet. Und ohne Feedback verliert auch der Programmierer die Lust daran.</p>
<p>Wie fängt man nun Fehler und Wünsche von den Benutzern ein? Die verbreitetste Lösung ist ein Bug- oder Issue-Tracker wie Mantis oder Bugzilla. Auch Github legt <a href="http://github.com/aaronmueller/advent2009/issues">für jedes Projekt einen Issue-Tracker</a> an. Hiermit lassen sich Fehler und Wünsche direkt über den Browser melden. Meist werden die Meldungen dankend angenommen, ein Paradebeispiel ist <a href="https://bugs.launchpad.net/ubuntu">Ubuntu</a>. Hier werden alle Fehlerberichte sehr ernst genommen und mit akribischer Genauigkeit bearbeitet, auch wenn die Bearbeitung etwas langsam ist.</p>
<p>Merken wir uns also: Wenn ein Open Source Projekt einen Fehler erzeugt, der so nicht vorkommen darf, dann freut sich der Autor sehr darüber, davon zu hören. Bug-Reports sind ein wichtiges Feature von Open Source das man nutzen sollte!</p>
<p>Was passiert aber jetzt, wenn das Projekt keine wirkliche Verbreitung erhält oder der Hauptentwickler aus irgend welchen Gründen aufhört an der Software zu arbeiten? Ist dies schlimm? Meistens leider ja, denn eine Software, die nicht in Bewegung ist, erhält meist auch keine wichtigen Bug Fixes mehr und stirbt somit. Es ist also wichtig, ein Open Source Projekt kontinuierlich zu betreuen und weiter zu entwickeln. Ist dies nicht der Fall, stirbt meist das Projekt.</p>
<p>Fühlt sich allerdings ein User so verbunden mit der Software, dass er beschließt das Projekt weiterzuführen, kann er dies natürlich tun. Man nennt dies auch <q>forken</q>. Man nimmt sich den letzten Stand der Software, überführt es in ein neues Repository und nennt es ein bisschen anders, meist wird ein <q>-ng</q> (Next Generation) hinten angehängt. Dies ist allerdings die letzte Rettung für ein Projekt. Besser ist es natürlich, wenn der Hauptentwickler die Leitung einer anderen Person überträgt, die dann das Projekt weiter trägt. Dies kommt glücklicherweise auch oft vor. Ziel ist es also, das Projekt in ständiger Bewegung zu halten, nie die Lust daran zu verlieren und auch Durststrecken durchzustehen.</p>
<p>Schwer ist dies natürlich dann, wenn jemand anders auf die gleiche Idee gekommen ist und eine ähnliche Software programmiert hat und schon weiter ist. Wenn zudem seine Software besser und verbreiteter ist, passiert es schnell, dass die eigene Software hierbei vom großen Riesen verschluckt wird. An solchen Stellen kann man sich die Frage stellen: <q>Weitermachen oder aufhören?</q> Eine dritte Option wäre auch, sein eigenes Wissen und vielleicht sogar Teile des Programmcodes in das andere Open Source Projekt zu übertragen und ein noch besseres Programm zu schaffen.</p>
<p>Allerdings ist hier auch wieder Vorsicht geboten: Zu viele Köche verderben den Brei. Jeder hat seine eigenen Vorstellungen, wie etwas umzusetzen ist und man muss innerhalb des Projekts eine gemeinsame Lösung finden. Leider passiert es häufig, dass dann beide Ziele verfolgt werden und am Ende eine Software mit 457 Einstellmöglichkeiten, 5 Oberflächen in 2 verschiedenen Programmiersprachen programmiert wird, mit der sich dann keiner mehr gerne beschäftigt. Sendmail ist (abgesehen von der sicherheitskritischen Architektur) ein gutes negativ Beispiel dafür.</p>
<p>Es gibt so viel zu beachten und man möchte es möglichst jedem Recht machen, doch leider ist dies nicht immer möglich. Im Endeffekt sollte man auf sich hören und das Umsetzen, was man selbst für richtig hält. Denn im Endeffekt ist die treibende Kraft die eigene Freude am Programmieren.</p>
<p>Damit möchte ich nun den Adventskalender und somit auch die Open Source Artikelreihe beenden. Doch aber nicht dieses Projekt, das zwischen den Artikeln entstanden ist. Wer also Lust hat, in den kommenden Feiertagen etwas zum Projekt beizutragen, kann dies gerne tun, ich würde mich sehr darüber freuen. Ein herzliches Dankeschön geht an Florian Eitel, der jede Nacht den Adventskalender neu gemalt hat und mit seiner Vorstellung der CLI-Tools eine perfekte Ergänzung zu meinen Beiträgen geliefert hat!</p>
<p>Am Ende würde mich jetzt eure Meinung interessieren. Wie fandet ihr die Reihe? Unter welchen Voraussetzungen würdet ihr mitmachen? <a href="http://surveys.polldaddy.com/s/6B6AC1EA4B4BC2D7/">Bitte, bitte nehmt an der Umfrage teil</a>! Ich würde mich sehr über ein Feedback freuen.</p>
<p>Ich wünsche allen ein schönes Weihnachtsfest, ein paar erholsame Tage und viel Erfolg im neuen Jahr! Danke fürs Lesen!</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/186
2009-12-21T23:00:00+01:00
2009-12-21T23:00:00+01:00
Open Source (Advent2009): Tag 22: Deployment und Portierung auf andere Systeme
<p>Das Spiel ist (theoretisch) fertig. Klar, alles wurde nur ansatzweise programmiert, aber gehen wir mal davon aus, dass wir nun eine stabile Version haben, die wir der Öffentlichkeit zeigen wollen. Wir müssen jetzt dafür sorgen, dass wir den Programmcode unters Volk bekommen und eine möglichst große Zielgruppe ansprechen. Deshalb werden wir uns noch ein paar Gedanken über Portierung machen.</p>
<p>Um unser Spiel auf einem Rechner zu installieren, muss der User zunächst die Software per Git herunterladen, danach compilieren und die so erzeugten Dateien in die richtigen Verzeichnisse legen. Dies ist ziemlich umständlich, sogar für uns. Bauen wir also zunächst unser Makefile etwas um, so dass wir es etwas leichter haben.</p>
<p>Fügen wir zwei neue Tasks hinzu. </p>
<pre><code class="cpp">install: compile man
cp $(OUTDIR)/$(NAME) /usr/local/bin/
cp $(OUTDIR)/$(NAME).6.gz /usr/local/man/
uninstall:
rm /usr/local/bin/adventgame || true
rm /usr/local/man/adventgame.6.gz || true
</code></pre>
<p>Der <code>install</code> Task hängt von compile und man ab, sprich bevor wir installieren können, müssen wir den Sourcecode compilieren und die Manpage packen. Die Zielverzeichnisse sind hier noch händisch eingetragen, dies sollten wir schnellstmöglich über einen Flag dem Benutzer überlassen. Beim <code>uninstall</code> Task löschen wir einfach die beiden kopierten Dateien.</p>
<p>Somit hätten wir schon mal die erste Hürde genommen, doch die Benutzer müssen sich immer noch mit Git auskennen und in der Lage sein, unser Projekt auszuchecken. Um dies zu vereinfachen, erstellen wir einen weiteren Task in unserem Makefile, der uns ein Archiv erzeugt, in dem alles wichtige enthalten ist.</p>
<pre><code class="cpp">export: clean
mkdir -p $(OUTDIR)/adventgame-$(VERSION)/
cp -r src/ doc/ dist/ Makefile $(OUTDIR)/adventgame-$(VERSION)/
cp README.rdoc $(OUTDIR)/adventgame-$(VERSION)/README
tar cjf $(OUTDIR)/adventgame-$(VERSION).tar.bz2 -C $(OUTDIR)/ adventgame-$(VERSION)
</code></pre>
<p>Wir packen alles notwendige zusammen, benennen die <code>README.rdoc</code> um und versehen alles mit einer Versionsnummer. Dies ist essenziell, da wir hier Git verlassen und somit auch die Versionierung. Dem Sourcecode sieht man ohne eine Versionsnummer nicht mehr an, ob er neuer oder älter ist als eine andere Version. Diese Datei können wir nun wieder auf Github hochladen, und jeder kann diese Datei direkt über den Browser herunterladen.</p>
<p>Da wir schon beim Hürden einreißen sind, wäre es natürlich toll, wenn wir nicht nur den Sourcecode herausgeben, sondern auch ein schon fertig compiliertes Binary. Die allermeisten Linux-Distributionen bieten hier einen Paketmanager an, unter OSX werden Image-Files verwendet und unter Windows die Installer (oder wie nennt man die setup.exe dort?).</p>
<p>Für <a href="http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=1#doc_chap1">Gentoo Linux </a>habe ich bereits angefangen, ein ebuild zu erstellen, für <a href="https://wiki.ubuntu.com/PackagingGuide/Basic?action=show&redirect=HowToBuildDebianPackagesFromScratch">Ubuntu</a> und <a href="http://wiki.freegeek.org/index.php/Basic_Debian_Packaging">Debian</a> ist dies auch nicht mehr sehr viel mehr. Es muss hier im Grunde nur eine Konfigurationsdatei angelegt werden, die das Paket beschreibt und die einzelnen Schritte wie compilieren, installieren usw. festlegt. Wer also möchte, kann mir bei der Erstellung eines Packages für seine Distribution gerne helfen. Solche Personen werden Maintainer genannt und sind übrigens sehr gefragt. Wenn man einmal den Dreh raus hat, ist es nicht mehr schwer und macht richtig Spaß! Man muss hierfür nicht am Projekt mitwirken, man muss noch nicht einmal programmieren können.</p>
<p>Gehen wir noch einen Schritt weiter: Um Pakete für OSX und Windows zu schnüren, müssen wir die Software irgendwie auf den Zielplattformen compilieren, da der Kernel des jeweiligen Betriebssystems nichts (oder nur sehr umständlich) mit einem Linux Binary anfangen kann.</p>
<p>Damit wir nicht drei oder mehr parallele Entwicklungszweige warten müssen, versuchen wir den Sourcecode so zu ändern, dass er einfach überall compiliert. Dazu können wir ein paar Präprozessordirektiven verwenden, um festzustellen, auf welcher Plattform wir uns befinden. Ein kleines Beispiel:</p>
<pre><code class="cpp">#ifdef __unix__ || __linux__
// Spezielle Befehle nur für Linux
#elif __WIN32__ || _MSC_VER
// Spezielle Befehle nur für Windows
#endif
</code></pre>
<p>Somit können wir beispielsweise verschiedene Header-Dateien einbinden, je nach dem auf welcher Zielplattform wir uns befinden. Etwas kniffliger wird es, wenn wir z.B. unser Spiel <a href="http://wiibrew.org/wiki/Homebrew_development">auf die Wii portieren</a> wollen. Hier gibt es ganz andere Eingabegeräte und offensichtlich keine Tastatur im herkömmlichen Sinne. Auch die Ausgabe verläuft etwas anders, da wir hier nur ein großes <q>Vollbild</q>-Fenster haben.</p>
<p>Um dieses Problem zu lösen, könnten wir Makros verwenden. Dazu wieder ein kleines Beispiel:</p>
<pre><code class="cpp">#ifdef __wiippc__
#define GET_WIDTH() (...)
#else
#define GET_WIDTH() (800)
#endif
</code></pre>
<p>Die Funktion <code>GET_WIDTH()</code> kann hier unterschiedliches zurückliefern, je nach dem für welche Plattform wir compilieren. In manchen Projekten wird diese Methode ziemlich häufig verwendet, was den Code zwar leichter portierbar macht aber auch etwas unübersichtlicher gestaltet. Man sollte sich also genau überlegen, an welchen Stellen es Sinn ergibt und an welchen Stellen man lieber eine andere Technik verwendet.</p>
<p>Der große Vorteil an den Präprozessordirektiven ist der, dass im eigentlichen Binary nur der Plattformspezifische Code enthalten ist, der Rest wird schon vor dem Compilieren weggeworfen.</p>
<p>Das Portieren des Spiels auf Windows und OSX habe ich jetzt nicht gemacht. Ebenso das Erstellen der Linux Packages steht noch aus, da zähle ich auf euch. Am letzten Tag werden wir noch einmal zusammenfassen, was wir geschafft haben. Zudem werde ich noch einen kleinen Ausblick geben und ein paar Aspekte ansprechen, die wir hier noch gar nicht beleuchtet haben, die aber alle noch in das Gebiet von Open Source mit einfließt. Freut euch drauf.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/184
2009-12-19T23:00:00+01:00
2009-12-19T23:00:00+01:00
Open Source (Advent2009): Tag 20: Testen, Dokumentation und User Contributions
<p>In den letzten Teilen ging es ziemlich zur Sache. Wir haben eine Menge programmiert, überlegt und konstruiert. Nun wollen wir sicherstellen, das das, was wir gemacht haben, funktioniert und andere auch verstehen. Deshalb werden wir unseren Code testen, dokumentieren und auch eine kleine Bedienungsanleitung schreiben. Da nicht nur ich selbst Code schreibe, sondern auch Patches und andere Anfragen von Benutzern kommen, müssen wir hier auch sicherstellen, diese mit in die Software einfließen zu lassen.</p>
<p>Zuerst werden wir uns unseren Programmcode vornehmen. Bis jetzt ist vor lauter Hackerei kaum Zeit für das Testen übrig geblieben. So ist es meistens und viele hören jetzt auf. Das Spiel ist im groben fertig, doch haben wir es nur auf unserem Rechner gestartet, keine Tests gemacht und nichts dokumentiert. Würden wir es so auf SourceForge, FreshMeat und Co. präsentieren, würden es zwar ein paar anschauen aber gleich wieder wie eine heiße Kartoffel fallen lassen. Alles was wir jetzt haben ist ein Berg Code, mit dem nur wir etwas anfangen können.</p>
<p>Wollen wir aber zuerst prüfen, was wir zuvor fabriziert haben. In den meisten objektorientierten Programmiersprachen sind UnitTests sehr verbreitet. Da wir aber in C weder Objekte noch Klassen haben, ist auch der Programmierstil ein etwas anderer und wir können nicht so einfach UnitTests schreiben. Trotzdem wollen wir unseren Code testen.</p>
<p>Zwar gibt es einige Test Frameworks für C, doch wir wollen uns etwas anderes anschauen, es nennt sich Assertion, also zu deutsch Zusicherung. Dieses Feature ist in der Standardbibliothek von C in der assertion.h versteckt. Hiermit wollen wir das Herzstück unseres Spiels Testen, die <code>drawPixel()</code> Funktion. Dies ist die empfindlichste Stelle im ganzen Code, denn hier dürfen wir uns keinen Fehler leisten, sonst stimmt die Darstellung nicht oder es finden gar hässliche Zuweisungen statt, die nicht erlaubt sind. Prüfen wir also, ob die Eingabewerte in den von uns gewünschten Bereichen befinden:</p>
<pre><code class="cpp">void drawPixel(int x, int y, int color) {
assert(x < SCREEN_WIDTH && x >= 0);
assert(y < SCREEN_HEIGHT && y >= 0);
// [...]
}
</code></pre>
<p>Mit dem <code>assert()</code> Makro können wir also überprüfen, ob eine Bedingung wahr ist oder nicht. Hier prüfen wir, ob sich x und y auf dem Bildschirm befindet.</p>
<p>Lassen wir das Programm ein paar mal laufen. Vermutlich sehen wir nicht viel, da der Fehler gerade jetzt nicht eintritt, deshalb müssen wir ihn provozieren bzw. die Funktion mal richtig durchtesten. Wir können entweder eine kleine Schleife um die <code>generateTerrain()</code> Funktion bauen oder ein kleines Testprogramm schreiben.</p>
<p>Als nächstes wollen wir uns um die Kommentare im Code kümmern. Hier hat jeder so seine eigenen Vorstellungen wie man richtig kommentieren sollte und in jeder Sprache gibt es einen quasi-Standard zum richtigen Kommentieren von Code, bei C gibt es natürlich wieder mehrere, wer hätte es gedacht. Kurzum, kommentiert kann wie man es für richtig hält, aber man sollte konsistent bleiben. Wer aus irgend einem Grund später mal <q>Dokumentation</q> aus dem Code heraus extrahieren will, sollte sich an die von dem entsprechenden Tool vorgegebenen Konventionen halten.</p>
<p>Ich habe in der <code>main.c</code> schon angefangen. Kommentare sollten nicht die einzelnen Zeilen Code beschreiben, sondern den Sinn dahinter. Jeder der die C-Syntax versteht, kann den Code lesen, doch bringt es ungemein mehr, wenn man den Gedankengang und die Intension des Programmierers versteht, warum er was wie und warum so gelöst hat. Dies sind viel wichtigere Informationen als zu wissen, dass auf eine Variable eins drauf addiert wird.</p>
<p>Damit unserer Programm eine runde Sache wird, schreiben wir natürlich auch noch eine Bedienungsanleitung dafür. Natürlich als Manpage, so wie es sich gehört. Manpages werden in (t|g)roff-Syntax geschrieben, was etwa so aussieht:</p>
<pre><code class="cpp">//[...]
.SH DESCRIPTION
The
.B adventgame
is the resulting output from the advent calendar series
from the website
.I advent.aaron-fischer.net
(in the year 2009).
//[...]
</code></pre>
<p>Eine ausführliche Beschreibung dazu findet man <a href="http://www.fnal.gov/docs/products/ups/ReferenceManual/html/manpages.html">hier</a>. Der Vorteil daran ist, dass wir daraus nicht nur eine Manpage generieren können, sondern auch andere Formate wie HTML oder PostScript.</p>
<p><img src="/images/20091218_manpage.png" alt="Manpage von adventgame"></p>
<p>Zum Schluss werden wir noch ein weiteres Thema beleuchten, von dem Open Source lebt: Den Beiträgen der Benutzer der Software. Da der Code von allen einsehbar ist, kann auch jeder Änderungen vornehmen. Leider ist es so, dass der Prozentsatz, der etwas zu einem Open Source Projekt beisteuert, im Promillebereich liegt. Die meisten sind schlicht zu faul ihre privaten Änderungen an den Autor zu schicken oder gar erst nach dem Fehler zu suchen. Deshalb muss man es den Benutzern so einfach wie möglich machen.</p>
<p>Bisher gab es leider nur eine Einsendung von Florian Eitel. Ich vermute aber mal, dass es an der Jahreszeit liegt. Im Dezember hat man einfach nicht die Zeit, die man im November oder im Januar hat, deshalb hoffe ich auf den Januar :)</p>
<p>Nachdem wir unser Projekt nun aufpoliert und für die Öffentlichkeit nutzbar gemacht haben, können wir es präsentieren. Doch eines fehlt noch, wir müssen es noch zu einem ganzen Paket zusammenschnüren. Wir müssen also unser Makefile etwas überarbeiten und ein <code>make install</code> hinzufügen. Dann bauen wir uns noch ein Debian Package daraus. Zuletzt werden wir uns noch überlegen, was wir tun müssen, um unser Spiel auf andere Plattformen zu portieren. Dazu übermorgen mehr.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/183
2009-12-17T23:00:00+01:00
2009-12-17T23:00:00+01:00
Open Source (Advent2009): Tag 18: Menü und Game States
<p>Soweit ist unsere Spielfläche fertig. Zwar bewegt sich noch nicht all zu viel, doch bevor wir jetzt mit dem Programmieren weiter fortfahren, sollten wir uns Gedanken über die Struktur machen. Momentan steckt ziemlich viel in der <code>draw.c</code> und der Rest ist in <code>main.c</code>. Auch landen wir beim Start der Anwendung direkt auf dem Spielfeld. Ein Menü wäre wünschenswert sowie etwas mehr Ordnung im Code.</p>
<p>Machen wir zuerst etwas Ordnung. Hierzu möchte ich ein Konzept einführen, das viele Spiele verwenden. Bei den meisten Spielen gibt es anfangs ein (mehr oder weniger verschachteltes) Menü, dann den eigentlichen Spielscreen und in diesem meist noch einmal eine Ebene, in der sich Dinge einstellen lassen oder aber sogar eine Konsole. Diese Zustände, in dem sich das Spiel befinden kann, nennt man Game States. Jeder Game State hat eine separate Eingabe und eine separate Ausgabe. Beispielsweise kann man mit den Pfeiltasten im Hauptmenü durch das Menü wandern während die Pfeiltasten im Spielscreen einen Geschützturm lenken. Auch brauchen wir eine separate Zeichenroutine für das Hauptmenü und für die Spielfläche.</p>
<p>Es muss also eine Möglichkeit her, in der für jeden Zustand Eingabe, Verarbeitung und Ausgabe separat abgehandelt wird. Dies lässt sich mit einer State Machine - oder auch Zustandsautomat genannt - gut umsetzen. Bei unserem kleinen Spiel wollen wir uns vorerst auf folgende Zustände beschränken:</p>
<p><img src="/images/20091216_gamestates.png" alt="Game States"></p>
<p>Wir haben ein Hauptmenü, von dem aus wir entweder das Spiel verlassen können, die Credits anschauen oder ein neues Spiel starten. Befinden wir uns im Credits-Bildschirm, können wir mit dem Zurück-Knopf wieder zum Hauptmenü zurückkehren. Wollen wir ein neues Spiel starten, werden wir in einem zweiten Menü (einem anderen Zustand) wählen können, mit wie vielen Spielern wir das Spiel spielen wollen. Mit <q>Zurück</q> kommen wir wieder ins Hauptmenü. Ist das Spiel einmal gestartet, können wir es mit ESC oder F10 beenden und wir erhalten noch eine Frage, ob wir wirklich beenden wollen.</p>
<p>Zusammengefasst müssen wir uns um fünf Zustände kümmern: <q>Hauptmenü</q>, <q>Spielerwahl</q>, <q>Credits</q>, das laufende Spiel und den <q>Spiel wirklich beenden?</q>-Dialog. Natürlich werden später noch weitere Zustände dazukommen, deshalb werden wir uns bemühen müssen, eine möglichst generische Lösung zu programmieren.</p>
<p>Da wir in C nicht einfach eine Zustandsklasse erstellen können und uns davon ein paar Instanzen in einer HashMap speichern können, müssen wir etwas weiter ausholen. Definieren wir uns zuerst unsere Zustände in einem enum:</p>
<pre><code class="cpp">enum states {
STATE_MAINMENU,
STATE_NUMPLAYERS,
STATE_CREDITS,
STATE_RUNNINGGAME,
STATE_RLYQUIT,
STATE_EXIT,
MAX_STATES
};
</code></pre>
<p><code>MAX_STATES</code> brauchen wir für die Anzahl der Zustände, <code>STATE_EXIT</code> ist ein Spezialzustand, mit dem wir das Spiel beenden können, er löst quasi unsere <code>gameRunning</code> Variable ab. Definieren wir uns weiter ein struct, das einen Spielzustand widerspiegelt. Wir haben oben gesagt, dass wir jeweils eine separate Eingabe- und Ausgabefunktion benötigen. Diese speichern wir als Funktionszeiger.</p>
<pre><code class="cpp">struct gameState {
void(*drawFun)();
void(*handleEventsFun)();
};
</code></pre>
<p>Anschließend erstellen wir uns eine Tabelle mit allen Zuständen. Das vordere ist jeweils die Ausgabe, das hintere die Events/Eingabe. Zudem definieren wir uns noch eine Variable, die den aktuellen Zustand enthält. Dieser weisen wir am Anfang den Zustand <q>Hauptmenü</q> zu, da der Startpfeil darauf zeigt.</p>
<pre><code class="cpp">struct gameState stateTable[] = {
{*displayMainmenu, *eventsMainmenu}, /* STATE_MAINMENU */
{*displayNumplayers, *eventsNumplayers}, /* STATE_NUMPLAYERS */
{*displayCredits, *eventsCredits}, /* STATE_CREDITS */
{*displayRunninggame, *eventsRunninggame}, /* STATE_RUNNINGGAME */
{*displayRlyquit, *eventsRlyquit}, /* STATE_RLYQUIT */
{*exitGame, *exitGame} /* STATE_EXIT */
};
enum states currentState;
currentState = STATE_MAINMENU;
</code></pre>
<p>Starten wir das Spiel, landen wir also im Hauptmenü-Zustand. Nun müssen wir dafür sorgen, dass die Funktionen <code>displayMainmenu()</code> und <code>eventsMainmenu()</code> aufgerufen werden. Dies machen wir in unserem Game Loop:</p>
<pre><code class="cpp"> while (currentState != STATE_EXIT) {
// Check for events
if (SDL_PollEvent(&event)) {
// Make it possible to close the game window
if (event.type == SDL_QUIT) currentState = STATE_EXIT;
stateTable[currentState].handleEventsFun();
}
// Draw the stuff on the screen and "flip" th the next frame
SDL_FillRect(screen, NULL, 0x000000);
stateTable[currentState].drawFun();
SDL_Flip(screen);
}
</code></pre>
<p>Die while-Schleife wurde nun ganz schön klein. Sie läuft logischerweise so lange, bis wir uns im Zustand <code>STATE_EXIT</code> befinden (unser einziger Endzustand im Diagramm). Zuerst schauen wir nach, ob ein Event vorliegt. Wenn ja, rufen wir die <code>handleEventsFun</code> des aktuellen Zustands auf. Da wir nach dem Starten im Hauptmenü sind, ist das die Funktion <code>eventsMainmenu()</code>. In <code>eventsMainmenu()</code> kann nun auf den events-Zeiger zugegriffen werden und auf Events reagieren. Im Hauptmenü wären das beispielsweise Pfeil hoch, Pfeil runter, Enter, usw. Danach wird der Bildschirm geleert und die <code>drawFun()</code> des aktuellen Zustands wird aufgerufen - in unserem Fall <code>displayMainmenu()</code>. In dieser Funktion wird auf den screen-Pointer zugegriffen, um auf dem Bildschirm zu malen. Hier werden wir das Hauptmenü zeichnen.</p>
<p>In einer Objektorientierten Programmiersprache sähe dies natürlich etwas hübscher aus, doch wir haben uns ja vorgenommen, nur mit C99 zu arbeiten, also müssen wir mit dem zurechtkommen, was wir haben: <code>enum</code>s, <code>struct</code>s, Funktionen und Zeiger auf diese. </p>
<p>Kommen wir zum Menü. Hier kommt uns das Zustandsmodell recht gelegen, da wir direkt davon profitieren können. Jedes Menü ist im Grunde gleich aufgebaut: Es gibt eine Liste von Einträgen (Strings), die jeweils einen Zustandswechsel vornehmen. Also verwenden wir wieder ein struct, um die Daten zu kapseln:</p>
<pre><code class="cpp">struct menuItem {
char buttonDescription[30];
enum states targetState;
};
</code></pre>
<p>Damit können wir einen Menüeintrag abbilden. Ein ganzes Menü sieht dann so aus:</p>
<pre><code class="cpp">struct menuItem mainMenu[] = {
{"Neues Spiel", STATE_NUMPLAYERS},
{"Credits", STATE_CREDITS},
{"Spiel beenden", STATE_EXIT}
};
</code></pre>
<p>Dies können wir direkt aus dem Zustandsdiagramm vom Anfang übernehmen. Die Transition ist die Beschriftung und der Zielzustand ist der Zustand, an dem der Pfeil endet. So können wir sämtliche Menüs definieren, die uns noch fehlen.</p>
<p>Da jedes Menü ein Zustand ist, hat es natürlich wieder eigene Eingaben und Ausgaben. Da aber jedes Menü gleich reagiert (Eintrag selektieren oder zum vorherigen springen) und die gleichen Ausgaben hat (Liste mit den Menüeinträgen, das aktuelle markieren), können wir hier wieder eine etwas generalisiertere Lösung angehen; wir definieren uns die Events und die Ausgaben eine Funktion mit Parametern.</p>
<p>Bei der Ausgabe kommt allerdings wieder etwas Neues ins Spiel. Wir müssen Text auf dem Screen zeichnen. Dies von Hand zu machen, wäre viel zu aufwändig, ein Bild nehmen und die Buchstaben daraus herausschneiden wollen wir nicht, also greifen wir auf <code>SDL_TTF</code> zurück, mit dem wir fertige Schriftarten einlesen und zeichnen können. Um dies zu tun, müssen wir wie bei SDL TTF initialisieren. Danach nehmen wir uns eine System-Schriftart. Hier könnte man wieder etwas optimieren und die Schriftart erst im System suchen, bevor man sie leichtfertig verwendet, wer also Lust hat, ... :)</p>
<pre><code class="cpp">TTF_Init();
menuFont = TTF_OpenFont("/usr/share/fonts/corefonts/verdana.ttf", 50);
void drawMenu(struct menuItem items[], int numItems) {
SDL_Surface *text;
SDL_Rect targetPos;
SDL_Color colorSelected = {0, 255, 66};
SDL_Color colorNormal = {59, 71, 62};
for (int i=0; i<<numItems; i++) {
targetPos.x = 200;
targetPos.y = i*60+200;
targetPos.w = SCREEN_WIDTH-200;
targetPos.h = 50;
text = TTF_RenderText_Solid(menuFont, items[i].buttonDescription,
currentMenuActionState == i ? colorSelected : colorNormal);
SDL_BlitSurface(text, NULL, screen, &targetPos);
}
}
</code></pre>
<p>Wir nehmen unser Menü, lassen es durch eine Schleife laufen und erstellen für jeden Eintrag die Schrift auf dem <code>text</code> Surface. Dieses Surface <q>mergen</q> wir nun mit <code>SDL_BlitSurface()</code> in unseren Screen.</p>
<p>Zum Schluss noch die Sache mit den Events. Wie schon oben geschrieben, müssen wir nicht viel darüber wissen, nur um welches Menü es sich handelt, wie viele Einträge es hat und was der vorherige Zustand ist.</p>
<pre><code class="cpp">void handleMenuEvent(struct menuItem items[], int numItems, enum states prevState) {
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_DOWN:
case SDLK_j:
currentMenuActionState++;
if (currentMenuActionState >> numItems-1) currentMenuActionState = 0;
break;
case SDLK_UP:
case SDLK_k:
currentMenuActionState--;
if (currentMenuActionState << 0) currentMenuActionState = numItems-1;
break;
case SDLK_RETURN:
case SDLK_SPACE:
currentState = items[currentMenuActionState].targetState;
currentMenuActionState = 0;
break;
case SDLK_ESCAPE:
case SDLK_BACKSPACE:
currentMenuActionState = 0;
currentState = prevState;
break;
default:
break;
}
}
}
</code></pre>
<p>Je nachdem welche Taste gedrückt wird, reagieren wir anders darauf. Wird Pfeil runter oder j gedrückt, setzen wir den nächst unteren Eintrag auf den aktuellen Eintrag. In die andere Richtung geht es mit Pfeil nach oben und k. Drücken wir Enter oder die Leertaste, setzen wir den aktuellen Zustand auf den Zielzustand des gerade selektierten Eintrags. In die andere Richtung geht es mit ESC und Backspace, hier setzen wir den aktuellen Zustand auf den übergebenen vorherigen Zustand <code>prevState</code>.</p>
<p>In den entsprechenden <code>display</code>- und <code>handleEvent</code>- Funktionen brauchen wir jetzt nur noch die Funktion mit den entsprechenden Parametern aufzurufen. Mit Hilfe des Zustandsautomaten haben wir mit ein paar Zeilen Code ein einfaches und erweiterbares Menüsystem gebaut.</p>
<p><img src="/images/20091216_neues_spiel.png" alt="Hauptmenü"></p>
<p>Dies war jetzt ganz schön viel auf einmal. Ich habe mit Absicht so viel in diesen Beitrag gepackt, damit ich noch ein paar andere Aspekte von OpenSource zeigen kann. Der komplette Code (also nicht nur die hier gezeigten Schnipsel) liegt wie immer auf GitHub. Übermorgen geht es um das Testen unserer Software, um Dokumentation, Bedienungsanleitung und der Umgang mit Patches, Wünschen und Anfragen von Endusern. Ich freue mich wie immer auf Kommentare und pull requests.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/185
2009-12-16T23:00:00+01:00
2009-12-16T23:00:00+01:00
Projekte: RSS Feed update
<p>Falls sich jemand wundert, warum sein Feedreader alte Posts nun plötzlich als brandneue Posts markiert: Das liegt daran, dass ich einen kleinen Fehler im RSS Feed repariert habe (das Preface der Artikel werden jetzt auch angezeigt, danke an <a href="http://beatrice-fischer.de/">Bea</a> und <a href="http://benjamin-schweizer.de/tumblelog/">Benjamin</a> für den Bug-Report!) und sich die Titel aller Posts geändert haben. Also keine Panik, der einzige neue Artikel ist dieser hier :)</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/182
2009-12-15T23:00:00+01:00
2009-12-15T23:00:00+01:00
Open Source (Advent2009): Tag 16: Farben und Interaktion
<p>So wie die Landschaft jetzt ist, sieht sie noch nicht sehr einladend aus. Wir brauchen also noch etwas Farbe. Was momentan auch noch etwas schlecht gelöst ist, ist die Tatsache, dass wir den Landschaftsgenerator nicht richtig testen können, da wir jedes Mal das Programm aufrufen und mit STRG + C wieder schließen müssen. Hier wollen wir uns auch etwas mehr Komfort gönnen.</p>
<p>Gehen wir zuerst an die Farben, dies gestaltet sich recht einfach. Da wir die Y-Werte der Landschaft ja schon haben, müssen wir nur in verschiedenen Abschnitten verschiedenfarbige Striche zeichnen. Ich habe mich für eine grüne Landschaft entschieden, eine Schneelandschaft (passend zur Jahreszeit!) wäre natürlich auch denkbar.</p>
<pre><code class="cpp">void drawScreen() {
Uint32 sky = SDL_MapRGB(screen->format, 186, 215, 217);
Uint32 green = SDL_MapRGB(screen->format, 101, 200, 21);
Uint32 darkGreen = SDL_MapRGB(screen->format, 52, 99, 14);
Uint32 ground = SDL_MapRGB(screen->format, 124, 88, 10);
for (int x=0; x<SCREEN_WIDTH; x++) {
int y = terrain[x];
drawLine(x, 0, x, y, sky);
if (y <= SCREEN_HEIGHT-15) drawLine(x, y, x, y+15, green);
if (y <= SCREEN_HEIGHT-20) drawLine(x, y+15, x, y+20, darkGreen);
if (y <= SCREEN_HEIGHT-21) drawLine(x, y+20, x, SCREEN_HEIGHT, ground);
}
}
</code></pre>
<p>Es werden zuerst ein paar Farben für den Himmel, die Grasnarbe und das Erdreich erzeugt. Dies wäre natürlich außerhalb der <code>drawScreen()</code> Funktion besser aufgehoben, doch zur Übersicht habe ich es noch in der Funktion gelassen. Darunter haben wir die gleiche for-Schleife wie zuvor, doch nun zeichnen wir vier separate Linien. Zuerst der Himmel, dann die beiden Grüntöne und dann den Rest in braun.</p>
<p>Hier könnte man nun entweder noch weitere Abstufungen einfügen (beispielsweise am Himmel oder im Erdreich), oder aber andere Elemente einbauen. Man könnte z. B. mit ein paar Kreisen Wolken in den Hintergrund zeichnen, die langsam vorbeiziehen oder kleine Maulwurfgänge in die Erde oder einfach nur ein Muster.</p>
<p>Eine weitere Verbesserung wäre die Grasnarbe, hier könnte man einzelne Grashalme einzeichnen, in dem man bei jedem X-Wert auf Y-Höhe eine zufällig lange Linie zeichnen würde. Der Zufallswerte sollte dann natürlich zwischen ca. 3 und 10 liegen, sonst würden die Proportionen nicht mehr stimmen. Wer sich also hier austoben möchte, nur zu!</p>
<p><img src="/images/20091214_landschaft_color.png" alt="eingefärbte Landschaft"></p>
<p>Weiter geht es mit der versprochenen Interaktion. Wir wollen mit einem Tastendruck neue Landschaften generieren. Dies ist nicht sehr schwer, da wir das Generieren der Landschaft schon in einer Funktion ausgelagert haben. Zuerst wieder der Programmcode:</p>
<pre><code class="cpp"> // the main game loop
while (gameRunning == true) {
// Check for events
if (SDL_PollEvent(&event)) {
// Make it possible to close the game window
if (event.type == SDL_QUIT) gameRunning = false;
// Keyboard interaction
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_RETURN:
free(terrain);
terrain = generateTerrain(150.0, 180.0);
printf("New terrain generated.\n");
break;
case SDLK_ESCAPE:
case SDLK_F10:
gameRunning = false;
break;
default:
break;
}
}
}
</code></pre>
<p>Einiges davon ist ja schon bekannt. Wir haben ja bereits den <code>SDL_QUIT</code> Event abgefangen, um das Programm sauber zu beenden, wenn jemand das X des Fensters drückt oder das Programm mit STRG + C beendet. Nun wollen wir uns den <code>SDL_KEYDOWN</code> Event etwas genauer anschauen. Dieser wird immer ausgelöst, wenn der Benutzer eine Taste nach unten drückt, genau das was wir brauchen. Über <code>event.key.keysym.sym</code> erhalten wir die Taste, die gedrückt wurde.</p>
<p>Wird die Taste <code>SDLK_RETURN</code> gedrückt (also die Entertaste), wollen wir eine neue Landschaft generieren. Hierzu geben wir den reservierten Speicher für die Landschaft wieder frei und generieren eine neue. Da die <code>drawScreen()</code> Funktion auf diese zugreift, müssen wir hier nicht mehr machen.</p>
<p>Um das Programm beenden zu können, definieren wir die Tasten ESC und F10 (wie in vielen Spielen üblich). Wenn diese gedrückt wird, setzen wir wieder <code>gameRunning</code> auf <code>false</code>, um aus dem Game Loop auszusteigen.</p>
<p>Übermorgen bringen wir etwas Ordnung in unser Spiel. Wir werden ein Hauptmenü erstellen und uns mit Game States beschäftigen. Freut euch drauf.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/181
2009-12-13T23:00:00+01:00
2009-12-13T23:00:00+01:00
Open Source (Advent2009): Tag 14: Die mathematisch beschriebene Landschaft
<p>Widmen wir uns nun wieder dem eigentlichen Spiel zu. Das Erste, was wir versuchen, ist ein zweidimensionales Profil einer Landschaft, ähnlich wie in den Spielen <a href="http://www.mobygames.com/game/tank-wars_/screenshots">Tank Wars</a>, <a href="http://www.mobygames.com/game/artillery-duel/screenshots">Artillery Duell</a>, <a href="http://www.mobygames.com/game/win3x/bang-bang/screenshots">Bang Bang</a> oder <a href="http://www.mobygames.com/game/worms/screenshots">Worms</a>. Hier gibt es natürlich mehrere Ansätze, so ein Landschaftsbild zu generieren. Das dies mehr oder weniger realistisch gemacht werden kann, sieht man schon an den Screenshots. Wir wollen zwei verschiedene Varianten ausprobieren.</p>
<p>Es gibt natürlich viele Möglichkeiten eine solche Landschaft zu generieren, doch die Zufallskomponente ist in jeder drin. Die Frage ist hier nur, an welchen Stellen man den Zufall einbaut und wie man die Landschaft generiert.</p>
<p>Eine einfache Variante ist das zufällige Überlagern von Sinuswellen. Wer schon einmal mit einem Audiobearbeitungsprogramm gearbeitet hat, kennt das vielleicht. Hier mal ein einfaches Beispiel:</p>
<p>Ich habe MuPad (einem Algebra-Programm) verwendet, allerdings kann man hier auch jedes andere beliebige Programm nutzen. Ein solches Programm hilft einem in diesem Stadium ungemein, um kleine Versuche zu unternehmen und schnell Daten zu visualisieren. Natürlich kann man hier auch GnuPlot plus eine bevorzugte Skriptsprache wählen um das gleiche zu erreichen.</p>
<p><img src="/images/20091214_landschaft_mupad.png" alt="Landschaft in MuPad"></p>
<p>So sieht es bei mir nach etwas herumprobieren aus. Ich habe mich entschlossen, drei Sinus-Kurven zu mischen, deren Verschiebung Höhe und Länge von einem Zufallsfaktor abhängig ist. Wer sich meine Versuche anschauen möchte, kann sich das MuPad-File anschauen, es liegt im snippets-Verzeichnis.</p>
<p>Um mit dem Zufall zu arbeiten, muss man zuerst vernünftige Zufallszahlen erzeugen können. Dafür gibt es die Funktion <code>rand()</code>, die eine zufällige Integer-Zahl erzeugt. Da wir aber Zufallszahlen in einem bestimmten Intervall haben wollen, habe ich diese etwas erweitert (ich weiß, der Platz in der <code>draw.c</code> Datei ist noch etwas unglücklich, aber das kann man ja noch ändern.</p>
<pre><code class="cpp">float _random(float from, float to) {
return ((to-from)*((float)rand()/RAND_MAX))+from;
}
</code></pre>
<p>Damit auch wirklich bei jedem Start eine neue Zufallszahl generiert wird, müssen wir den Zufallsgenerator zuerst anwerfen und mit einem Startwert initialisieren. Als Startwert nehmen wir einfachheitshalber den aktuellen Zeitstempel.</p>
<pre><code class="cpp">time_t start; srand(start);
</code></pre>
<p>Nachdem die Zufallszahlen stehen, können wir uns an die Implementierung der Landschaft machen:</p>
<pre><code class="cpp">int* generateTerrain(float peakheight, float flatness) {
time_t start; srand(start);
float offset = (float)SCREEN_HEIGHT/2;
float r1 = _random(1.0, 5.0);
float r2 = _random(1.0, 5.0);
float r3 = _random(1.0, 5.0);
int *yvals = (int*)malloc(SCREEN_WIDTH*sizeof(int));
for (int x=0; x<SCREEN_WIDTH; x++) {
float y;
y = peakheight/r1*sin((x/flatness*r1)+r1);
y += peakheight/r2*sin((x/flatness*r2)+r2);
y += peakheight/r3*sin((x/flatness*r3)+r3);
y += offset;
yvals[x] = (int)y;
}
return &(yvals[0]);
}
</code></pre>
<p>Eigentlich eine recht übersichtliche Funktion, bis auf das * und das &, aber dazu gleich mehr, zuerst die Funktion selbst: Wir generieren drei Zufallszahlen für unsere Sinuswellen und reservieren für die Y-Werte der Landschaft genügend Speicher (Wir brauchen Fensterbreite*(Größe eines Integers) Platz für die Werte). Danach generieren wir für jeden X-Werte den entsprechenden Y-Wert, in dem wir zu y drei Sinuswellen mit unterschiedlichen Zufallswerten addieren. Der Offset ist nur dazu da, damit in unserer Landschaft keine Flüsse entstehen. Am Ende speichern wir den Y-Wert als Integer im zuvor reservierten Speicher.</p>
<p>Nun zum Rückgabewert, der sieht etwas komisch aus. Bei der Funktionsdefinition haben wir ja schon kenntlich gemacht, dass die Funktion einen Pointer zurück gibt, dies machen wir hier auch. Wir nehmen uns das erste Element von yvals und lesen mit dem & die Adresse davon aus, also den Zeiger auf das erste Element des Arrays. Da wir genau wissen, wie groß unser Array ist, können wir dies bedenkenlos machen, wüssten wir die Länge nicht, könnten wir am anderen Ende mit dem Pointer nicht viel anfangen, da wir nicht wüssten, was und wie viel sich dahinter verbirgt.</p>
<p>Nun müssen wir nur noch das Array erzeugen und mit einer simplen for-Schleife anzeigen. Die beiden Werte <code>peakheight</code> und <code>flatness</code> sind zusätzliche Parameter, um die Landschaft zu verändern.</p>
<pre><code class="cpp">terrain = generateTerrain(150.0, 180.0);
for (int x=0; x<SCREEN_WIDTH; x++) drawLine(x, terrain[x], x, SCREEN_HEIGHT, white);
</code></pre>
<p>Fertig sieht es beispielsweise so aus.</p>
<p><img src="/images/20091213_landschaft.png" alt="Zufällig generierte Landschaft"></p>
<p>Dies war jetzt eine Möglichkeit, eine Landschaft zu erzeugen. Ich habe mich etwas umgesehen und noch weitere spannende Möglichkeiten entdeckt. Eine Davon wird <a href="http://www.java-applet.de/Landscape.html">in diesem Tutorial beschrieben</a>. Man nehme sich einen ersten zufälligen Punkt. Der nächste Punkt daneben variiert um 1px nach oben oder nach unten, dabei fällt der Zufallsgenerator 90% in die Richtung des vorherigen und 10% in die andere, so kann man spitze Berge und Täler erzeugen.</p>
<p>Eine weitere sehr interessante Möglichkeit ist es, eine Fraktaltechnik zu verwenden. <a href="http://gameprogrammer.com/fractal.html">Auf gameprogrammer.com</a> gibt es einen sehr guten Einstieg dazu. Die Grundidee ist folgende: Man zeichnet zuerst eine horizontale Linie in einer zufälligen Höhe. Dann teilt man diese Linie in der Mitte und verschiebt die beiden so neu entstandenen Punkte zufällig nach oben oder unten, als würde man mit zwei Fingern ein Gummiband nach oben ziehen oder nach unten drücken. Danach fährt man mit den beiden neu entstandenen Linien gleichermaßen fort, bis man den gewünschten Detailgrad erwünscht hat.</p>
<p>Eine weitere sehr interessante Technik ist die <q>Perlin Noise</q> Technik. <a href="http://freespace.virgin.net/hugo.elias/models/m_perlin.htm">Hier</a> und <a href="http://wiki.delphigl.com/index.php/Perlin_Noise">hier</a> wird das ziemlich gut erklärt. Es werden hier zufällige Punkte in ein Koordinatensystem gezeichnet und die fehlenden Punkte zwischendrin durch Interpolation <q>dazugerechnet</q>. Hätte man die zufälligen Punkte als Matrix vorliegen, könnte man auch <a href="http://de.wikipedia.org/wiki/Methode_der_kleinsten_Quadrate">die Methode der kleinsten Quadrate</a> verwenden, um ein Polynom hohen Grades daran anzugleichen. Dies würde aber erheblich mehr Rechenleistung und Programmieraufwand bedeuten.</p>
<p>Soweit steht also unsere Landschaft. Der komplette Sourcecode steht natürlich wieder <a href="http://github.com/aaronmueller/advent2009/commit/5b4847d15c390df9e05b52ab8ad8150f539db905">in Github zur Verfügung</a>. Wer sich an einer weiteren Methode versuchen möchte, kann dies gerne tun, ich würde mich sehr darüber freuen. Übermorgen werden wir uns etwas ums Ambiente kümmern. Momentan sieht die Landschaft noch sehr trist aus.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/180
2009-12-11T23:00:00+01:00
2009-12-11T23:00:00+01:00
Open Source (Advent2009): Tag 12: Die eigene Toolbox und Code debugging
<p>Heute wollen wir richtig in die Programmierung mit C einsteigen. Die Umgebung steht, die IDE ist aufgebohrt, der Build-Prozess automatisiert und der Rahmen steht auch schon. Nun gehen wir daran, uns ein kleines Set an Grafikbefehlen zusammenzubauen, mit denen wir Linien, Flächen, Rechtecke oder Kreise zeichnen können. Spätestens jetzt werden wir zwangsläufig Fehler machen. Damit wir die Fehler auch schnell lokalisieren und beheben können, schauen wir uns den Debugger <em>gdb</em> an.</p>
<p>Zuerst geht es an unsere Grafik Bibliothek. Da wir keine Grafiken einbinden wollen, brauchen wir ein paar andere Hilfsmittel, um eine grafische Komponente in das Spiel zu bringen. Dafür wäre es nicht schlecht, wenn wir Grundelemente wie Linien, Rechtecke (gefüllt oder nicht) und Kreise zeichnen können. Machen wir uns solche Funktionen!</p>
<p>Zuerst wollen wir uns an der Linie versuchen. Hier wollen wir eine Linie zwischen zwei Punkten ziehen. Leider reicht es nicht ganz aus, eine Geradengleichung mit der Zwei Punkte Form aufzustellen und diese zu zeichnen, denn je nach Neigungswinkel der Geraden müssen wir sie anders zeichnen. Das folgende Bild verdeutlicht das etwas:</p>
<p><img src="/images/20091209_gerade.png" alt="Gerade"></p>
<p>Wir können also nicht in X-Richtung iterieren, wenn die Gerade zu steil verläuft. Aus diesem Grunde müssen wie entsprechend wechseln. (Deshalb auch die signum-Funktion und der kompliziertere Aufbau). Die Funktion ist nicht komplett von mir, teile hab ich <a href="http://lexoft.eu/asphyxia/new/tut3.txt">von diesem alten Dokument</a> übernommen.</p>
<pre><code class="cpp">int _sign(float a) {
return a > 0 ? 1 : (a << 0 ? -1 : 0);
}
void drawLine(int x1, int y1, int x2, int y2, int color) {
float u, s, v, d1x, d1y, d2x, d2y, m, n;
int x = x1, y = y1;
u = x2-x1;
v = y2-y1;
d1x = d2x = _sign(u);
d1y = _sign(v);
d2y = 0;
m = abs(u);
n = abs(v);
if (m <= n) {
d2x = 0;
d2y = _sign(v);
m = abs(v);
n = abs(u);
}
s = (int)(m/2);
for (int i=0; i<round(m); i++) {
drawPixel(x, y, color);
s += n;
if (s >= m) {
s -= m;
x += d1x;
y += d1y;
} else {
x += d2x;
y += d2y;
}
}
}
</code></pre>
<p>Mit der Möglichkeit, eine Gerade zu zeichnen, können wir uns dem nächsten Problem widmen: Dem Rechteck. Wollen wir ein gefülltes Rechteck, zeichnen wir einfach Y viele Linien mit X Länge unter einander. Wollen wir nur die Umrandung, zeichnen wir vier einfache Linien. Ziemlich einfache Geschichte. </p>
<pre><code class="cpp">void drawRect(int x1, int y1, int x2, int y2, int color, bool filled) {
int tmp;
if (x1 > x2) { tmp = x1; x2 = x1; x1 = tmp; }
if (y1 > y2) { tmp = y1; y2 = y1; y1 = tmp; }
if (filled) {
for (int i=y1; i<=y2; i++) drawLine(x1, i, x2, i, color);
} else {
drawLine(x1, y1, x2, y1, color);
drawLine(x1, y1, x1, y2, color);
drawLine(x2, y1, x2, y2, color);
drawLine(x1, y2, x2, y2, color);
}
}
</code></pre>
<p>Beim Zeichnen des Kreises zähle ich auf euch!
<em>(Vorsicht Spoiler!) Zwei Lösungsansätze: Entweder man zeichnet den Kreis wie ein Stern mit sehr vielen Zacken. Also eine Linie Zeichnen, diese Linie um 2 Grad nach oben rotieren und dort zeichnen usw. Eine weitere Möglichkeit ist es, einen Kreis mit Durchmesser 1 zu zeichnen, dann mit Durchmesser 3 usw.</em></p>
<p>Mit diesen Funktionen können wir schon recht viel anstellen. In der <code>drawScreen()</code>-Funktion habe ich ein kleines <q>Testbild</q> gebastelt, die die Funktionen testet. Wollen wir das Testen etwas weiter treiben. Probieren wir mal das hier aus:</p>
<pre><code class="cpp">for (int i=0; i<=600; i++) drawLine(0, i, 799, i, white);
</code></pre>
<p>Compilieren wir den Source uns starten es, schmiert es nach dem Start direkt mit der Meldung <q>Segmentation fault</q> ab. Hiermit können wir natürlich nicht viel anfangen. Ein erster Schritt wäre es jetzt, in den Sourcecode zu schauen und nach dem Fehler zu suchen. Doch ohne Anhaltspunkt gestaltet sich dies recht schwer. (Klar, wir wissen ja schon, dass es irgend was mit der gerade eingefügten Zeile zu tun haben muss, hat man aber mehrere Änderungen gemacht, die sich wieder auf andere Codeteile auswirken, ist es nicht mehr so offensichtlich).</p>
<p>Der zweite Ansatz wäre, ein paar <code>printf()</code>-Funktionsaufrufe in den Code zu streuen und sich dann durch den Output zu wühlen um daraus Schlüsse zu ziehen. In unserem Fall ist dies allerdings etwas ungünstig, da wir viele Ausgaben in jedem Frame (30-100 pro Sekunde?) haben. Die Ausgaben würden nur so über den Bildschirm rasen. Eine bessere Lösung muss her.</p>
<p>gdb ist ein Debugger, der den Binärcode eines Binaries so zerpflückt, dass man die Anwendung kontrollieren kann. Damit gdb so viele Informationen wie möglich aus den compilierten Binary ziehen kann, kann man gcc die Option <code>-ggdb</code> mitgeben. Damit werden Debugging-Information ins Binary mit eingebaut. Das compilierte Binary wird dadurch etwas größer, doch dieses kann man mit <strong>strip</strong> wieder schrumpfen lassen.</p>
<p>Starten wir das Programm noch einmal mit gdb:</p>
<pre><code class="cpp">$ gdb ./bin/game
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <<http://gnu.org/licenses/gpl.html>>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
(gdb)
</code></pre>
<p>Wir landen in einer interaktiven Console, mit der wir nun das Programm steuern können. Mit dem Befehl <strong>run</strong> starten wir es.</p>
<pre><code class="cpp">(gdb) run
Starting program: .../bin/game
[Thread debugging using libthread_db enabled]
[New Thread 0xb7bbc6d0 (LWP 19035)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7bbc6d0 (LWP 19035)]
0x0804899f in drawPixel (x=256, y=600, color=16777215) at src/draw.c:13
13 p[lineOffset+x] = color;
</code></pre>
<p>Hier sehen wir schon etwas mehr. Wieder den Segmentation fault Abbruch, doch nun die Zeile, in der es kracht. Hier sieht man auch, mit welchen Parametern die Funktion aufgerufen wurde. Mit <strong>backtrace</strong> können wir uns den Call-Stack anschauen, also die Reihenfolge, in der die fehlerhafte Funktion aufgerufen wurde.</p>
<pre><code class="cpp">(gdb) backtrace
#0 0x0804899f in drawPixel (x=256, y=600, color=16777215) at src/draw.c:13
#1 0x08048b6e in drawLine (x1=0, y1=600, x2=799, y2=600, color=16777215)
at src/draw.c:43
#2 0x08048fd4 in drawScreen () at src/draw.c:92
#3 0x0804907b in main () at src/main.c:23
</code></pre>
<p>Jetzt wird das Problem klar: In der <code>drawScreen()</code>-Funktion haben wir in Zeile 43 unsere Schleife, in der wir <code>drawLine(0, 600, 799, 600, 16777215)</code> aufrufen. Da die for-Schleife bis 600 läuft, stürtzt das Programm im letzten Durchlauf ab. Der eigentliche Fehler tritt dann in Zeile 13 in der Funktion <code>drawPixel()</code> auf. Diese wird mit x=256 und y=600 aufgerufen. Hier verbirgt sich das Problem. Da wir unseren screen nur mit 800x600 initialisiert haben (also x=0..799 und y=0..599) gibt es hier einen Überlauf. Dies wurde uns nicht gleich bewusst, da es sich um ein eindimensionales Array handelt. Wer mag sich um dieses Problem kümmern? :)</p>
<p>Bei kniffligeren Fällen kann man auch Breakpoints (Haltepunkte) einfügen und Schrittweise durch den Code steppen. Mehr dazu findet ihr in <a href="http://www.cs.cmu.edu/%7Egilpin/tutorial/">diesem kleinen Tutorial</a>.</p>
<p>So, dies waren wieder viele Informationen. Am besten alles mal ausprobieren und etwas herumspielen. Vielleicht hat sich ja noch der ein oder andere Fehler eingeschlichen. Den kompletten Sourcecode habe ich natürlich wieder auf Github hochgeladen. Übermorgen werden wir dann eine Landschaft generieren.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/179
2009-12-09T23:00:00+01:00
2009-12-09T23:00:00+01:00
Open Source (Advent2009): Tag 10: Open Source nutzen, SDL und Grundgerüst
<p>Open Source ist deshalb so erfolgreich, weil der Programmcode frei zugänglich ist und von jedem verwendet und erweitert werden darf. Und genau das werden wir jetzt auch machen. Da wir ein 2D-Spiel programmieren wollen, aber nicht bei Null anfangen wollen, greifen wir auf SDL zurück, welches uns den Zugang zur Grafikkarte erleichtert.</p>
<p><a href="http://www.libsdl.org/">SDL</a> steht für <q>Simple Directmedia Layer</q> und gibt uns eine Abstraktionsebene, von der aus wir auf die Hardware wie Grafikkarte, Eingabegeräte und Ausgabegeräte zugreifen können. Wir können also bequem einen Pixel auf den Bildschirm zeichnen, ohne uns darüber Gedanken zu machen, welche Grafikkarte der User gerade verwendet. Dies nimmt uns SDL ab.</p>
<p>Mit SDL können wir auch auf den 2D Framebuffer zugreifen, was unser Ziel sein wird. Hiermit können wir sehr schnell auf einer 2D-Oberfläche zeichnen. Da wir uns als Grundidee überlegt haben, Plattform unabhängig zu sein, ist SDL ideal dafür. Auf der Homepage ist eine vierzeilige Liste von Systemen, die unterstützt werden. (SDL funktioniert nicht nur mit C sondern kann von fast allen Sprachen verwendet wird. Das hier gelernte ist also nicht umsonst!)</p>
<p>Also fangen wir an. Die ersten haben schon das Projekt auf github geforked und schon commits eingereicht, prima! Wer das noch nicht getan hat: Jetzt ist die passende Gelegenheit dazu! Im git-Repository habe ich <a href="http://github.com/aaronmueller/advent2009/commit/e12b581fc895e5079300d1a541efd08443adfc6c">eine Basis angelegt</a>, auf der wir das Spiel entwickeln können. Das Projekt besteht noch nicht aus all zu vielen Dateien, so dass man sich schnell einen Überblick verschaffen kann. Florian hat zudem das Makefile aus dem vorletzten Artikel überarbeitet, so dass der Programmcode sauber vom Rest getrennt bleibt, danke dafür.</p>
<p>Schauen wir uns den Programmcode etwas näher an. Zuerst die main.c. Dies ist die <q>Startdatei</q>, in der sich auch die <code>main()</code>-Funktion befindet. In dieser Funktion initialisieren wir zuerst SDL mit <code>SDL_init()</code>. Da wir momentan nur die Grafikfunktionen von SDL benötigen, steht hier <code>SDL_INIT_VIDEO</code> als Parameter. Wollen wir weitere Features von SDL nutzen, können wir diese mit einem <q>oder</q> (|, Pipe) verknüpfen. Eine solche Technik wird eine Zeile weiter unten schon angewendet. Dies ist ziemlich gängig bei C-Programmen, da man so viele Optionen in einem einzigen Integer speichern kann. Jedes Bit steht für eine andere Option und durch ein binäres <q>oder</q> werden die einzelnen Bits gesetzt.</p>
<p>Weiter geht es mit <code>SDL_SetVideoMode()</code>, von dem wir einen Pointer auf ein <code>SDL_Surface</code> bekommen. Wir speichern uns diesen Pointer in der Variablen screen, da wir ihn noch oft brauchen werden. Dieser Pointer ist unser Zugang zu unserer Zeichenfläche. Die Option <code>SDL_HWSURFACE</code> steht dafür, dass die Zeichenfläche im Arbeitsspeicher der Grafikkarte abgelegt wird, dies macht die Anwendung schneller. <code>SDL_DOUBLEBUF</code> verhindert hässliches Flimmern, dazu evtl. später mehr.</p>
<p>Dann kommt eine while-Schleife, in der das Programm/Spiel die restliche Zeit verbringen wird, bis es beendet wird. In ihr werden zuerst die Events abgefragt und dann auf den Bildschirm bezeichnet. Danach rufen wir <code>SDL_Flip()</code> auf, um den nächsten Frame zu zeichnen. Diese Schleife stellt unser Spiel dar: Events abfragen, Berechnungen machen (gerne auch als Logik-Teil bezeichnet), aufs Display zeichnen, Frame anzeigen, Events abfangen, berechnen, zeichnen, Frame anzeigen, und immer so weiter ...</p>
<p>Wie vielleicht schon gemerkt, habe ich hier eine config.h und eine draw.h eingebunden. In der config.h bzw. config.c sind globale Variablen und Konstanten definiert die wir in den anderen Dateien benötigen, so wie die Variable screen. Dies war notwendig, da wir jede einzelne c-Datei einzeln compilieren und anschließend linken.</p>
<p>In der Datei draw.c habe ich bereits begonnen, ein Minimalset an Funktionen zu implementieren, um auf dem Bildschirm zu zeichnen. Die Funktion <code>drawScreen()</code> wird bei jedem Frame aufgerufen. Die Funktion <code>drawPixel()</code> zeichnet einen einzelnen Pixel an die angegebene Stelle. Bis auf die <code>drawPixel()</code>-Funktion müsste alles weitgehend Selbsterklärend sein, wenn nicht, würde ich mich sehr über einen kurzen Kommentar freuen, so dass ich das dann nachtragen kann. Die <code>drawPixel()</code>-Funktion möchte ich jetzt im Detail erklären, da diese etwas kniffliger ist.</p>
<pre><code class="cpp">void drawPixel(int x, int y, int color) {
unsigned int *p = (unsigned int*)screen->pixels;
int lineOffset = y * (screen->pitch/4);
p[lineOffset+x] = color;
}
</code></pre>
<p>Zuerst einmal holen wir uns aus dem <code>SDL_Surface</code> Pointer screen, den wir zu Beginn in der <code>main()</code>-Funktion gesetzt hatten, einen Pointer auf ein Array, dass die Bildpunkte im Arbeitsspeicher der Grafikkarte repräsentiert. Hier merkt man schon, wie tief man trotz Abstraktionsschicht abtauchen kann. Nun wird aber auf der Grafikkarte nur mit binärdaten gearbeitet. So wird beispielsweise für ein Fenster mit <code>800x600</code> Pixeln Platz für <code>1024x600</code> Pixel reserviert. Warum das so ist, sieht man an folgendem Bild recht gut:</p>
<p><img src="/images/20091207_pitch.png" alt="pitch"></p>
<p><code>1024</code> sind <code>2^10</code>, <code>2^9</code> wären <code>512</code>, also nicht ausreichend für <code>800</code> Punkte. Somit sind <code>224*600*4 Bit = ~0.5 Mb</code> Speicher einfach verschenkt, doch das soll uns nicht weiter stören. Wichtig ist für uns jetzt, wie wir die Punkte korrekt auf das Fenster zeichnen. Da in unserem Fall p ein Pointer auf ein eindimensionales Array ist, können wir auf dieses auch wie ein solches darauf zugreifen. Um die Position zu bestimmen, werden wir uns zuerst den Offset berechnen, also wie viele Zeilen wir nach unten gehen müssen. <code>screen->pitch</code> gibt uns die Anzahl Bytes, die eine Zeile (Scanline) hat. Da wir die Farbtiefe auf 32 Bit gestellt haben, braucht jedes Pixel 4 Byte (<code>32 Bit = 2^4 = 4 Byte</code>). Deshalb teilen wir <code>screen->pitch</code> durch 4 und multiplizieren es mit der Anzahl Zeilen (y). Darauf addieren wir x und setzen an genau dieser Position unsere 4 Byte - also unsere Farbe. Wie die Farbe definiert wird sieht man in der <code>drawScreen()</code>-Funktion. Am Besten einfach mit dem Cursor über <code>SDL_MapRGB()</code> gehen und <strong>K</strong> drücken.</p>
<p>Wie ihr seht, geht es schon recht lowlevel zu, so dass es uns recht gelegen kommt, wenn wir eine simple Funktion haben, deren wir x, y und eine Farbe geben und diese uns ein Pixel auf den Bildschirm malt. Müssten wir uns jetzt noch um all die verschiedenen Grafikkarten und deren Speichermanagement kümmern, würden wir schnell die Lust daran verlieren.</p>
<p>Soweit soll es für heute erst einmal gewesen sein. Macht euch bis übermorgen vertraut mit dem Sourcecode und falls ihr gerne etwas optimieren oder ändern wollt, lasst es mich über Github wissen. Ich freue mich über jede Beteiligung. Auch ein anonymer Kommentar zeigt mir, dass die Artikel gelesen werden :)</p>
<p>Übermorgen geht es dann um das Zeichnen von Linien, Rechtecken und Kreisen. Wir werden uns eine kleine Sammlung von Befehlen zurechtlegen, mit denen wir dann weiterarbeiten können. Wir werden zudem den Debugger gdb verwenden, um einem <q>Segmentation fault</q> mehr als nur einen Programmabsturz zu entlocken. In der Zwischenzeit sind natürlich Code-Optimierungen (mit Sourcecode-Kommentaren vorausgesetzt) und Verbesserungsvorschläge herzlich willkommen. Ich gebe auch sehr gerne Hilfestellungen hier in den Kommentaren, falls etwas nicht so klappen sollte wie hier beschrieben.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/177
2009-12-07T23:00:00+01:00
2009-12-07T23:00:00+01:00
Open Source (Advent2009): Tag 8: Vim als IDE, Basics für C-Programmierung
<p>Um vernünftig programmieren zu können, brauchen wir natürlich eine professionelle IDE, die uns unterstützt und hilft. Unter Java ist das einfach, hier fällt die Wahl schnell auf Eclipse oder NetBeans, bei C sieht das schon etwas anders aus. Da es so viel Auswahl gibt, habe ich mich für Vim entschieden, da es auch außerhalb von C sehr nützlich ist, sich einmal mit diesem Editor beschäftigt zu haben.</p>
<p>Vim ist schon mehrere Jahrzehnte alt und stammt wiederum von einem anderen Editor ab. Doch bis heute ist und bleibt er der Standard (neben Emacs und Nano) auf jeder Linux-Kiste. Ich habe bereits <a href="https://aaron-fischer.net/artikel/kategorie/vim-mastery">mehrere Vim-Tipps und Tricks</a> zusammengeschrieben, so dass ich hier keine große Einführung in den Editor geben will. Statt dessen will ich ein paar Tipps und Tricks für die Programmierung mit C aufzeigen, da wir das in den nächsten Tagen machen werden.</p>
<p>Ein sehr gutes Tutorial ist <a href="http://tldp.org/HOWTO/C-editing-with-VIM-HOWTO/index.html">C editing with VIM</a> von Siddharth Heroor. Das Dokument ist etwas alt, aber die Befehle sind so gut wie alle gleich geblieben. Ich werde hier nur eine kurze Zusammenfassung der wichtigsten Befehle geben, wer mehr wissen will kann sich gerne durch das oben erwähnte Tutorial durcharbeiten, es ist nicht all zu lang. Für diejenigen, die Vim zum ersten Mal verwenden sei der offizielle Vim-Tutor ans Herz gelegt. Einfach <q>vimtutor</q> auf der Console eingeben.</p>
<h2 id="konfiguration">Konfiguration</h2>
<p>Es gibt ein paar Regeln, die sich im Laufe der Jahre etabliert haben. Hierzu gehören, dass eine Zeile (C)-Code maximal 80 Zeichen lang sein darf. Dies hat den Vorteil, dass die Zeilen selbst nicht so kompliziert werden und man bei kleineren Displays nicht so viel scrollen muss. Natürlich sollte der Programmcode auch richtig eingerückt werden, dazu gibt es auch eine Option in Vim, um dies zu automatisieren (Einrückungen immer nur mit Leerzeichen, nicht mit Tabs). Schreibt folgende Optionen in eure .vimrc Datei:</p>
<pre><code class="cpp">set textwidth=80
set wrapwidth=60
set cindent
set number
set showmatch
set shiftwidth=2
set tabstop=2
set expandtab
set hlsearch
</code></pre>
<h2 id="bewegen">Bewegen</h2>
<p>Die üblichen Navigationstasten sind <strong>hjkl</strong> (die Cursortasten gehen natürlich auch, sind aber nicht zu empfehlen, da man ständig die Hand von der Tastatur nehmen muss), doch für horizontale Bewegungen gibt es einfachere Wege: Mit <strong>w</strong> und <strong>b</strong> navigiert man Wortweise, mit <strong>(</strong> und <strong>)</strong> in ganzen Blöcken. Ein sehr tolles Feature ist <strong>%</strong>, mit dem man zum gegenüberliegenden Zeichen springen kann. Die gegenüberliegenden Zeichen sind meist Klammern in Funktionsaufrufen oder die geschweiften Klammern für Codeblöcke. Ist der Cursor auf einer öffnenden Klammer und % wird gedrückt, springt der Cursor zur passenden schließenden Klammer.</p>
<h2 id="spr-nge-im-programmcode">Sprünge im Programmcode</h2>
<p>Sobald das Projekt aus mehr als einer Funktion oder einer Datei besteht ist man viel damit beschäftigt Dateien zu öffnen, zu Funktionen zu scrollen, wieder zu der Ursprungsstelle zu gehen, dort etwas zu editieren usw. Dies lässt sich mit dem Programm ctags und Vim etwas verbessern. Damit wir dieses Feature nutzen können, müssen wir zuerst folgendes aufrufen:</p>
<pre><code class="cpp">ctags-exuberant -R *.{c,h}
</code></pre>
<p>So werden alle Programmdateien eingelesen und analysiert. Heraus kommt eine Datei mit dem Namen <q>tags</q>, welche für jede Datei und Funktion im Programmcode deren Position speichert. (Dies wäre auch wieder ein toller Task für unser Makefile!) Steht der Cursor gerade über einem Funktionsaufruf und <strong>STRG + ]</strong> wird gedrückt, springt der Cursor direkt zur Funktionsdefinition. Mit <strong>STRG + t</strong> gehts wieder zurück. Also optimal um mal schnell zu schauen was die Funktion macht.</p>
<p>Ähnliches geht auch mit Variablen und deren Definitionen. Geht man über eine Variable und drückt <strong>gd</strong>, springt der Cursor direkt zur Definition. Mit dem <q>*</q> kann man allgemein das nächste Vorkommen des unter dem Cursor stehenden Wortes suchen.</p>
<h2 id="vervollst-ndigung-von-dateien-funktionen-und-variablen">Vervollständigung von Dateien, Funktionen und Variablen</h2>
<p>Natürlich will keiner etwas doppelt schreiben. Deshalb lässt sich mit der Tastenkombination <strong>STRG + p</strong> den gerade getippten Anfang einer Variablen, einer Funktion oder einer Datei vervollständigen. Gibt es mehrere Möglichkeiten, wird ein Dropdown-Feld angezeigt aus dem man wählen kann. Für lokale Dateien verwendet man <strong>STRG +x, STRG +f</strong>.</p>
<h2 id="compilieren-debuggen">Compilieren, Debuggen</h2>
<p>Da wir schon ein Makefile angelegt haben, brauchen wir hier nicht mehr viel zu beachten. In Vim lässt sich make durch <strong>:make [task]</strong> aufrufen. Treten Fehler auf, springt Vim direkt zum Ersten und der Fehler wird in der Statuszeile angezeigt. Mit <strong>:cn</strong> und <strong>:cN</strong> kann man zwischen den Fehlern springen.</p>
<h2 id="hilfe-zu-funktionen">Hilfe zu Funktionen</h2>
<p>Am Anfang steht man sehr häufig vor dem Problem, dass man nicht weiß, was eine entsprechende Funktion macht oder was für Parameter sie braucht. Alle C Funktionen und die Funktionen der Standardbibliotheken sind als Manual-Page schon im System. Ein Aufruf von <q>man scanf</q> ruft beispielsweise die Hilfeseite zur Funktion scanf auf. Mit <q>info scanf</q> erhält man einen mehr strukturierten Text, der evtl. für das Verständnis besser geeignet ist.
Geht man in Vim mit dem Cursor über einen Funktionsnamen und drückt <q>K</q> (großes k), öffnet sich die gleiche Seite. Mit q, ENTER kommt man wieder zurück.</p>
<p>Dies sollte fürs Erste genügen. Natürlich kann man all das gezeigte nicht an einem Tag verinnerlichen und im Schlaf beherrschen, aber es gibt immer ein Tag 1, an dem man zu lernen beginnt, in dem man es einfach immer versucht anzuwenden. Vielleicht ist ja heute Dein Tag 1?</p>
<p>Übermorgen geht es dann wirklich ans Programmieren. Wir werden uns die Grafik Bibliothek SDL anschauen und uns ein kleines Grundgerüst bauen, auf dem wir dann weiter aufbauen können.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/176
2009-12-05T23:00:00+01:00
2009-12-05T23:00:00+01:00
Open Source (Advent2009): Tag 6: C, Compiler, Makefile, Buildprozess
<p>Im Open Source Bereich ist die meiste Software in C oder C++ geschrieben. Dies hat mehrere Gründe: Zum einen bestehen viele der Programme schon mehrere Jahrzehnte und zum anderen macht es diese besonders kompatibel. Aus diesem Grunde werden wir das Spiel auch in reinem C(99) schreiben. Somit sind wir schnell, plattformunabhängig, haben eine stabile Basis und ein Monster von Compiler zur Verfügung.</p>
<p>Zuerst einmal sollten wir klären, was C eigentlich genau ist. Die Programmiersprache hat <a href="http://de.wikipedia.org/wiki/Varianten_der_Programmiersprache_C">mehrere Ausprägungen</a>, da sie mit den Jahren an ihren Anforderungen gewachsen ist und dennoch streng standardisiert ist. Wir werden uns hier auf die Variante C99 von 1995 (<a href="http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf">ISO/IEC 9899:1999</a>) stützen, weil der gcc so gut wie jede Plattform unterstützt und uns zum anderen im Vergleich zu den anderen Ausprägungen etwas mehr Komfort bietet.</p>
<p>Um C zu lernen empfiehlt es sich nicht, den 500 Seiten Standard zu lesen. Im Internet gibt es jede Menge besser lesbares Material dazu. Für Schnelleinsteiger würde ich das <a href="http://randu.org/tutorials/c/">C Programming Tutorial</a> von Alfred Park empfehlen, wer lieber ein Buch ließt sei <a href="http://www.librarything.de/work/1299419/book/28133718">C Programmieren von Anfang an</a> von Helmut Erlenkötter empfohlen. Ich bin recht gut damit zurechtgekommen, vermutlich gibt es aber mittlerweile viel bessere Bücher dazu.</p>
<p>Eine <a href="http://www.cppreference.com/wiki/c/all">Liste aller verfügbaren Funktionen</a> ist auch ganz hilfreich. Wer unter Linux arbeitet, hat die komplette Dokumentation schon installiert und kann über den Befehl <q>man</q> aufgerufen werden.</p>
<p>Ein minimales C Programm sieht so aus:</p>
<pre><code class="cpp">#include <stdio.h>
int main(int argc, char **argv) {
printf("Bald kommt der Nikolaus\\n");
return 0;
}
</code></pre>
<p>So, nachdem wir uns mit der Programmiersprache C beschäftigt haben und uns mit allerlei Resourcen ausgerüstet haben, geht es nun daran, den Programmcode zu übersetzen. Hierfür werden wir das überall eingesetzte Urgestein gcc verwenden. Dieser Compiler kann mittlerweile alle C und C++ Dialekte. Ein Aufruf sieht etwa so aus:</p>
<pre><code class="python">gcc -std=c99 -Wall -o game main.c
</code></pre>
<p>Das -std=c99 legt die C-Variante fest, das -W steht dafür Warnungen auszugeben und das all hinterher für alle. -o gibt die Ausgabedatei an, der letzte Parameter ist die Datei selbst.</p>
<p>Läuft soweit alles, machen wir uns daran, diesen Vorgang zu automatisieren, denn wenn es mehrere Dateien werden, wird es unübersichtlicher und immer umständlicher. Wir wollen das komplette Programm zukünftig mit einem einzigen Befehl zusammenbauen. Hierfür gibt es das bewährte Programm Make. Ähnlich wie Ant gibt es hier eine Konfigurationsdatei (Makefile) in der alle Tasks und Aktionen stehen. Für unser Minimalbeispiel von oben würde folgende Konfiguration ausreichen:</p>
<pre><code class="python">CC=gcc
CFLAGS=-Wall -Os -std=c99
all: main.c
$(CC) $(CFLAGS) -o game main.c
run:
./game
clean: game.o
rm game.o
</code></pre>
<p>Mit dem Aufruf <q>make all</q> können wir nun das Projekt compilieren und mit <q>make run</q> starten. <q>make clean</q> räumt auf. Der Compiler und die Optionen habe ich in einer Variable ausgelagert, da wir hier später evtl. an zentraler Stelle noch Änderungen vornehmen müssen. Zu beachten ist auch, dass die Einrückungen aus genau einem TAB bestehen müssen, nicht aus Leerzeichen.</p>
<p>So, es gab viel Neues dieses mal. Ich habe absichtlich nicht alles im Detail erklärt, da ich euch nicht langweilen wollte. Dennoch bin ich sehr froh über Fragen aller Art in den Kommentaren. Falls etwas nicht funktioniert so wie hier beschrieben, helfe ich gerne weiter. Der hier besprochene Programmcode ist auf GitHub und kann dort angeschaut werden. Habt ihr bereits einen Fork des Projektes gemacht, könnt ihr die Änderungen mit dem Befehl <q>git pull upstream master</q> in euer Projekt mit übernehmen. Das Makefile lässt nun Raum für weitere Tasks. Beispielsweise wäre ein <q>install</q>-Task toll, der das Binary nach /usr/local/bin/ kopiert oder ein <q>backup</q>-Task, der den Programmcode in ein tar.bz2-Archiv packt und nach /var/backups/ kopiert. Ich freue mich auf pull requests :)</p>
<p>Übermorgen geht es dann weiter mit der IDE. Wir werden uns Vim anschauen und einige Basics zur Programmierung mit C kennen lernen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/175
2009-12-03T23:00:00+01:00
2009-12-03T23:00:00+01:00
Open Source (Advent2009): Tag 4: Open Source Infrastruktur - GitHub
<p>Open Source Projekte stützen sich nach Möglichkeit auch auf freie Tools. Dies hat den angenehmen Vorteil, dass jeder auch etwas mit dem freien Programmcode anfangen kann. Gerade von OSS wird sehr viel Software-Engineering abverlangt, denn Projekte müssen nicht nur über Ländergrenzen hinweg funktionieren, sondern verlangen von den Teilnehmern noch viel mehr als man das von normalen Mitarbeitern in Software-Schmieden abverlangen würde. Dafür braucht es natürlich auch professionelle Tools.</p>
<p>Früher funktionierte das Fixen eines Fehlers und das anschließende Einreichen der Änderungen (man sagt hier auch <q>in den Upstream bringen</q> oder <q>einen Patch einreichen</q>) etwas umständlich: Man lud sich den Programmcode von SourceForge.net herunter, machte seine Änderungen und erzeugte mit diff eine Datei, die die gemachten Änderungen enthielt. Diese Datei schickte man dem Ersteller der Software, der diese mit dem Programm patch in seinen eigenen Programmcode einspielte. So läuft es in vielen Stellen noch heute. Das Genie Linus Torvalds hatte für den Linux Kernel eine professionelle Lösung für dieses Problem geschaffen. Git heißt das Programm und ist eine Versionsverwaltung der Generation 3.</p>
<p><a href="http://github.com/">GitHub</a> ist ein freier Hosting-Dienst für git-Repositories mit einer schicken Oberfläche, die die Kollaboration noch einfacher gestaltet. Diesen Dienst werden wir verwenden. Also schnell einen Account anlegen und das Projekt <a href="http://github.com/aaronmueller/advent2009">advent2009</a> forken. (Ein <q>fork</q> eines Projektes ist eine Abspaltung vom Hauptentwicklungszweig in ein neues Projekt, meist mit <q>-ng</q> Postfix im Namen. Früher eine Schande für den Hauptentwickler, mit GitHub gewollt!) Somit habt ihr erst einmal eine exakte Kopie meines Projektes. Dieses Projekt muss jetzt auf die Platte. Aber eines vorweg: Allein entwickeln ist viel einfacher, man sollte deshalb den Mehraufwand nicht unterschätzen. Aber wer die komplexen Tools einmal beherrscht, erkennt wie sie den Prozess verbessern und beschleunigen.</p>
<p>Ach ja, ich hatte vergessen zu erwähnen, dass ich hier davon ausgehe, dass ihr eine vernünftige Console habt. Unter Linux und OSX 10.x sowieso, für Windows gibt es <a href="http://www.cygwin.com/">Cygwin</a>.</p>
<p>Also, ist ein Fork des Projektes angelegt, wird es mit der git@ URI geclont.</p>
<pre><code class="cpp">git clone git@github.com:/deinName/advent2009.git
</code></pre>
<p>Danach ist es ratsam, den Upstream als entfernten Branch mit aufzunehmen. Der Upstream ist in diesem Falle das Projekt von mir.</p>
<pre><code class="cpp">git remote add upstream git://github.com/aaronmueller/advent2009.git
git fetch upstream
</code></pre>
<p>Ein <q>git branch -va</q> zeigt nun alle Branches an. Nun können wir Änderungen vornehmen. Die Änderungen selbst zeigt uns <q>git diff</q> an. Mit <q>git status</q> sehen wir die Dateien, die hinzugekommen, entfernt oder geändert wurden. Alle Änderungen, die commited werden sollen, müssen zuerst hinzugefügt werden, <q>git add <Datei></q> erledigt dies. Nachdem man mit <q>git commit -va</q> einen lokalen Commit durchgeführt hat, lädt man die Änderungen auf den Github server mit <q>git push origin master</q> wieder hoch. Auf Github sind nun die Änderungen für alle sichtbar.</p>
<p>Jetzt kommt wieder Github ins Spiel, was uns ein tolles Kollaborationsfeature bietet. Bei jedem Projekt auf Github gibt es einen Button, der sich <q>pull request</q> nennt. Drückt man diesen, kann man die gemachten Änderungen an den Hauptentwickler melden. Macht dies, wenn ihr Änderungen an mich schicken wollt. Traut euch, davon lebt Open Source!</p>
<p>Ein weiteres wichtiges Feature ist das Holen von Änderungen vom Hauptentwicklerzweig. Dazu kann man <q>git pull upstream master</q> verwenden. Dies holt die Änderungen aus dem Upstream und bindet es in den eigenen master-Branch ein.</p>
<p>Diese Basics sollten nun ausreichen um Git in den Grundzügen zu bedienen. Wie man schon merkt, ist Git ein sehr mächtiges und komplexes Tool. Es besteht aus über 100 kleinen Tools mit jeweils dutzenden von Optionen. Es braucht etwas Zeit bis man die Befehle verstanden und verinnerlicht hat, doch es lohnt sich - nicht nur für den Lebenslauf.</p>
<p>Übermorgen werden wir uns mit der Programmiersprache C und gcc beschäftigen, wir werden ein eigenes Makefile schreiben und damit den Buildprozess automatisieren. Gibt es Fragen oder Probleme? Dann fleißig die Kommentarfunktion nutzen.</p>
Aaron Fischer
tag:aaron-fischer.net,2005:Article/174
2009-12-01T23:00:00+01:00
2009-12-01T23:00:00+01:00
Open Source (Advent2009): Tag 2: Ideen, Konzepte, Ziele, Planung
<p>Jeder hat den Begriff Open Source schon öfter gehört und jeder hat so eine ungefähre Ahnung, was er den bedeutet. Die meisten stellen sich unter Open Source als Software vor die man ohne zu bezahlen herunterladen kann. Einige assoziieren damit sogar minderwertige Software, die sowieso von ein paar Freaks im Keller geschrieben wurden. Nur wenige kommen in den Genuss, Open Source in ihrer freien Wildbahn direkt mitzuerleben und mitzugestalten. Und weil es so viel Spaß macht, gibt es in diesem Bereich wahre Fanatiker, die am liebsten alles frei zugänglich machen wollen.</p>
<p>Aber zuerst einmal eine Definition, die sich für mich mit den Jahren herauskristallisiert hat: Open Source ist nichts anderes als Daten, die mit Absicht allen zur Verfügung gestellt werden. Doch warum sollte man das machen? Warum sollte ich die Arbeit, in die ich so viel Zeit gesteckt habe allen zugänglich machen und sie auch noch aufzumuntern es zu verändern? Um das zu verstehen, muss man sich Open Source etwas näher anschauen.</p>
<p>Von außen betrachtet sieht es so aus als wäre das total bescheuert; da sitzt einer bis nachts um 4 an einem Stück Programmcode und feilt daran herum bis es perfekt ist, nur um es dann irgendwo hochzuladen und darauf zu warten, dass es jemand anderes kopiert und umändert. Und tatsächlich kommt zwei Tage später jemand, kopiert sich den Code und ändert ihn ab. Warum sollte man so dämlich sein?</p>
<p>Warum? Weil es ein tolles Gefühl ist! Wie? In der Tat, die Open Source Szene ist brutal. Es überlebt immer nur das Beste vom besten. Doch genau das ist das Beste daran! Das macht die Sache so interessant! Einen Bug in einer großen Software zu fixen, ist wie einem Superstar die Hand zu schütteln. Wer es schafft, dass sein Werk die anderen verdrängt und als einziger bestehen bleibt, ist der Superstar. Und genau das ist doch die Definition von Superstar: Wildfremde Menschen helfen dir zu deinem Erfolg, Zeitschriften berichten über dich, Bücher werden über dich geschrieben, in Vorlesungen wird über dich geredet und täglich beschäftigen sich Millionen Menschen mit dir. Dieses Gefühl kann man nur haben, wenn man ein Superstar ist oder ein Open Source Projekt aus dem Boden stampft, das so erfolgreich wird wie vim, Apache oder der Linux Kernel. Und deshalb freut sich jeder Open Source Programmierer, wenn jemand anderes seinen Code kopiert, vervielfältigt und verbessert. Anerkennung und Spaß sind die treibenden Kräfte die so viele Menschen motiviert, sich die Nächte um die Ohren zu schlagen.</p>
<p>Da man dies erst glaubt, wenn man es selbst einmal miterlebt hat, möchte ich in den nächsten 22 Tagen und vielleicht sogar darüber hinaus einen kleinen Einstieg geben. Wir wollen uns in den nächsten Tagen mit den Tools und Techniken beschäftigen, aber auch selbst ein Open-Source-Projekt starten. Somit füllt sich euer Advent mit Tools wie Git und Vim, aber auch mit SDL und der Programmiersprache C.</p>
<p>Wir wollen zusammen ein kleines 2D-Spiel beginnen. Das Tolle an einem Open-Source-Projekt ist auch, dass man es nur einem selbst recht machen muss und damit fällt auch das Schreiben eines Pflichten- oder Lastenheftes weg. Es müssen keine Verträge eingehalten werden und die Features werden <q>on demand</q> definiert. Allerdings hat so gut wie jedes Projekt ein paar Grundgedanken, die es verfolgt. Unsere Grundeinstellungen sollen sein:</p>
<ul>
<li>Plattformunabhängig, leicht portierbar</li>
<li>Das Spiel soll mit einem einzigen Binary auskommen, also keine Bilder, Level-Dateien etc.</li>
</ul>
<p>Mit diesen Grundgedanken werden wir uns die nächsten Tage und Wochen immer wieder beschäftigen. Übermorgen gehts dann weiter mit der Infrastruktur.</p>
Aaron Fischer