Moment mal, wie lange sitze ich jetzt schon am Rechner und programmiere? Also meine ersten Erinnerungen belaufen sich so ungefähr auf 1987 zurück, d.h. also seit 30 Jahren. In dieser Zeit laufen einem viele Merkwürdigkeiten über den Weg, aber mit Visual Foxpro verbindet mich eine gewisse Hassliebe. Die Altvorderen kennen vielleicht noch dBASE als einen Vorgänger von Foxpro. Als ich anfing mich damit zu beschäftigen, bin ich schon über das Wunder des Stringvergleichs gestoßen, aber jetzt muss ich das mal festhalten. Vielleicht kann ich doch noch andere für diese archäologische Relikt begeistern.

weiterlesen

Ich bin ja seit einigen Wochen stolzer Besitzer einer Garmin vivoactive HR. Das ist nicht nur eine schicke Uhr, sondern auch ein erstklassiker GPS-Tracker. Leider fehlte mir bis jetzt noch ein schönes WatchFace (zu Deutsch eine Uhrendarstellung), die mir gefällt. Das Switch-Theme ist schon mal ganz gut, weil es Akkuladung, Herzschlag, kCal und gelaufene Strecke neben der Uhrzeit anzeigt, aber das Design ist verbesserungswürdig.

Also habe ich mir mal angeschaut, wie man so ein Watch Face erstellt. Es gibt prima Tutorials bei Garmin, die das einfach erklären. Die verwendete Sprache MonkeyC ist etwas gewöhnungsbedürftig. Sie soll eine angereicherte C-Variante sein, mutet aber wie Java mit schlechten Basic-Angewohnheiten (untypisierte Parameter) an. Kleine Kostprobe?

function writeText(dc, position, text, fontType) {
  dc.drawText(dc.getWidth() / 2, position, fontType, text, Gfx.TEXT_JUSTIFY_CENTER);
}

Zum Glück erklären auch die Tutorials, wie man das MonkeyC bzw. das Connect IQ in Eclipse einbindet, sonst musste man das auch noch über Textzeile steuern. Ganz so viel Retrocharme brauch ich dann doch nicht. Jedenfalls habe ich mir als Ziel gestellt, ein Watch Face zu erstellen, welches die Uhrzeit in Worten anzeigt. Auf der Webseite von Garmin gibt es auch ein Video, welches zeigt, wie man benutzerdefinierte Fonts einbindet. Und so entstand eine Uhr, die auch noch die Uhrzeit in einer Frakturschrift ausgibt.

Immer wieder habe ich zwischendrin mal eine Idee, was ich von meiner Plattenverwaltung noch gerne wissen möchte – so eine Art universelle Statistik. Zum Beispiel wissen, welche Platten noch kein Cover haben, wo noch kein Erscheinungsjahr hinterlegt ist usw. Völlig unabhängig davon entdeckte ich auf meiner virtuellen Maschine auf Arbeit einen Hinweis auf Microsoft Power BI. Wenn man das auflöst wird Business Intelligence daraus. In Kombination mit „Power“ klingt das richtig gruselig nach stundenlangen Besprechungen in den Fakten und Zahlen präsentiert werden. Fakt ist – genau dafür ist das Teil gedacht. Man verbindet sich mit einer Datenquelle, extrahiert die Daten – gruppiert, summiert und macht dann eine schicke Grafik daraus.

Also hab ich mich hingesetzt und der Power BI meine Plattenverwaltung zu futtern gegeben. Prima hat mir gefallen, dass man sofort das komplette Datenmodell sieht und die Beziehungen der Tabellen untereinander. Und genau entlang dieser Beziehungen kann man seine Abfragen aufbauen. Meine erste Frage an das System war: Wie oft höre ich Platten in Beziehung zu ihrem Erscheinungsdatum? Ich schnappte mir die Tabelle „PlayStats“, wo das Datum hinterlegt ist, wann eine Platte abgespielt wurde. Diese Tabelle verweist auf die Platte und diese wiederum auf die Veröffentlichung. Ich reduzierte das Abspieldatum auf Monat und Jahr, entfernte IDs und andere Daten, die nicht relevant sind und gruppierte dann nach dem Erscheinungsjahr, was eine neue Spalte hervorruft: Die Anzahl pro Monat und Erscheinungsjahr.

Jetzt kommt der optische Teil – bis jetzt starrt man nur auf langweilige Zahlenkolonnen, aber nachdem man die Daten fertig hat, kann man ähnlich wie in Excel Diagramme generieren und dabei live die Diagrammtypen ändern usw. Und fertig war mein erstes Diagramm: Eine Treemap, die zeigt, dass mehr als ein Drittel der Veröffentlichungen, die ich bisher (seit Einführung der Abspielfunktion) aus dem Jahr 2016 stammen und darunter – feiner aufgeteilt – die einzelnen Monate. Und auf die Schnelle noch ein zweites Diagramm: Anhand der Veröffentlichung sehen, wie sich die Verteilung des Mediums geändert hat.

Heute gibt es mal wieder was zu sehen. Nachdem ich das letzte Mal den Grundstein für meine Backup-Software gelegt habe, ging es jetzt darum, Stück für Stück die Software zu erweitern. Schritt 2 sieht vor, dass ich überprüfen möchte, ob meine Sicherung erfolgreich war. Wie macht man das am besten? Einfach vergleichen, ob die Dateigröße übereinstimmt? Nein, dass ist zu simpel – ich möchte wirklich sicher sein. Meine erste Idee war, dass ich beide Dateien (aus Quelle und Ziel) wirklich Byte für Byte miteinander vergleiche. Aber eine kleine Internetrecherche brachte mich auf eine andere Idee: Aus dem Inhalt beider Dateien einen MD5-Hash berechnen und den miteinander vergleichen. Die Chance, dass hier das Gleiche entsteht, ist zwar vorhanden, aber mit dem Risiko kann ich leben. Außerdem habe ich dann die Möglichkeit wenn ich später noch das inkrementelle Backup einbaue, dass ich an jedes Paket eine Datei mit den MD5-Hashes anhänge und mir den Vorteil erkaufe, nicht nur nachsehen zu müssen, ob an der Datei geändert wurde, sondern auch inhaltliche Änderungen registriere.

Und dann war mal wieder die Oberfläche dran. Ich musste wieder einige Klassen in meine gemeinsam genutzte Bibliothek verschieben, Namespaces korrigieren und dann konnte ich mich darum kümmern, dass ich das Quellverzeichnis auswählen kann. Und nun das Ergebnis von Schritt 3: Ich kann das Quellverzeichnis auswählen:

Im Zusammenhang mit dem MD5-Hash ist mir natürlich aufgefallen, dass ich da etwas mit dem Feuer spiele. Denn wenn ich große Dateien (z.B. 1GB) miteinander vergleiche, dann steigt mir das Programm mit einem Fehler aus: Out of memory. Bei 16GB Hauptspeicher etwas merkwürdig, aber um solche Fehler will ich mich erst kümmern, wenn das Backup bei mir in Echtbetrieb geht.

Ich habe mir am Sonntag ein bisschen Zeit genommen und mit der Programmierung angefangen. Klingt doch gar nicht so kompliziert: mal einen Ordner kopieren. Am Anfang habe ich erstmal ein paar Komponenten, die ich schon in der Plattenverwaltung verwende, in eine gemeinsam genutzte Bibliothek verschoben und musste natürlich in der Plattenverwaltung ein paar Referenzen korrigieren. So nach 2-3 Stunden hatte ich dann eine Oberfläche mit einem Button und eine wieder funktionierende Plattenverwaltung.

Für den Anfang stand ja folgendes fest:

  • kein Oberflächen-Schnick-Schnack – Button und fertig
  • Quelle und Ziel sind im Code verankert
  • Backup läuft synchron, d.h. keine Aktualisierung der Oberfläche während kopiert wird

Datenmodell brauchte ich diesmal nicht und konnte gleich loslegen. In meinem Kopf stiegen die Aufgaben wie kleine Bläschen hoch. Verzeichnis rekursiv auslesen, Liste von Dateien erstellen, Dateien kopieren. Schon allein für das Auslesen gibt es mehrere Wege und Strategien. Ich entschloss mich für die ausführliche Version, die mir zu einer Datei gleich die Attribute (Ist ein Verzeichnis? Ist eine temporäre Datei?) mitliefert. Strategie ist „Tiefe zuerst“, d.h. erstmal alle Verzeichnisse durchklappern und dort die Dateien einsammeln. Dann wurde es kurz kniffelig. Ich musste ja den Quellpfad nehmen und mit dem Zielpfad mischen. Also wenn ich z.B. den Ordner C:\Users\jan\Pictures nach E:\ sichern will, muss ich die Datei C:\Users\jan\Pictures\Island\tolles_bild.jpg nach E:\Pictures-2016-09-14_19-34\Island\tolles_bild.jpg kopieren. An der Stelle vielen mir drei Szenarios ein: 1. Verzeichnis nach Verzeichnis (easy – der Standardfall), 2. Verzeichnis nach Laufwerk (etwas Spaß mit den Pfadtrennern, aber auch noch einfach) und 3. irgendwas mit Netzwerkpfaden (erstmal nicht ausprobiert). Dass an den Zielpfad das Datum und die Uhrzeit rangeklebt wird, ist zwar etwas vorgegriffen, aber dann hab ich es schon mal drin.

Damit es überhaupt was zu sehen gibt, schon mal die Code-Map des Projekts. Momentan noch sehr übersichtlich…

Seit dem Desasterumbau in meinem Rechner, habe ich jede Woche ein Backup zu laufen. Manche Daten sichere ich wöchentlich, manche monatlich. Viele Sicherungen – gerade bei den Fotos – laufen inkrementell. Trotzdem kommt es immer mal vor, dass mehrere Vollsicherungen zu einem Zeitpunkt anstehen. Ich hocke nun auch nicht den ganzen Tag vor dem Rechner, um Backups laufen zu lassen, d.h. ich breche die Sicherungsaufträge ab, weil ich sie später wiederholen möchte. Auch wenn ich den Haken bei „Sicherung wiederholen“ in meiner Backup-Software gesetzt habe, wird die Sicherung nicht wiederholt. Was zur Folge hatte, dass ich nachfragte, warum denn die Sicherung nicht wiederholt wird. Als Antwort kam: Wenn der Auftrag gestartet wurde, gilt die Sicherung als gemacht. Entschuldigung, aber häää? Ich schrieb eine Antwort, die besagte, dass ich eine erfolgreiche Sicherung möchte und nicht eine gestartete Sicherung. Nach der Aussage kam nichts mehr…

Und nun? Selbst ist der Entwickler, also starte ich ein neues Projekt: meine eigene Backup-Software. Wie auch bei meiner Plattenverwaltung zählt hier – die Software soll meine Wünsche erfüllen und wer Lust hast, kann sie gern benutzen. Also habe ich mich mal hingesetzt und aufgeschrieben, wie ich meine Backup-Software schrittweise aufbaue:

  1. Ich möchte einen Ordner auf ein anderes Laufwerk sichern.
  2. Ich möchte Quelle und Ziel nach dem Backup miteinander vergleichen und sicher sein, dass beide identisch sind.
  3. Ich möchte den Ordner auswählen, den ich sichern möchte.
  4. Ich möchte das Ziellaufwerk auswählen, wo ich hinsichern möchte.
  5. Ich möchte die Konfiguration beim nächsten Programmstart zur Verfügung haben.
  6. Ich möchte einen Hinweis erhalten, wenn das Ziellaufwerk nicht zur Verfügung steht.
  7. Ich möchte den Fortschritt des Auftrags kontrollieren können.
  8. Ich möchte den Auftrag abbrechen können.
  9. Ich möchte den Status des letzten Auftrags einsehen können (erfolgreich, abgebrochen, fehlerhaft).
  10. Ich möchte mehrere Aufträge mit Konfiguration (Quelle, Ziel) anlegen können.
  11. Ich möchte, dass das Backup zu einer bestimmten Uhrzeit monatlich startet.
  12. Ich möchte, dass der Auftrag wiederholt wird, wenn er nicht erfolgreich durchgeführt wurde.
  13. Ich möchte die Uhrzeit einstellen können und in meinem Auftrag hinlegt haben.
  14. Ich möchte das Intervall einstellen und mit dem Auftrag speichern können.
  15. Ich möchte auf dem Ziellaufwerk Platz sparen und die Sicherung in einer komprimierten Datei ablegen.
  16. Ich möchte die Anzahl meiner Sicherungen limitieren, d.h. alte Sicherungen werden entfernt.
  17. Ich möchte die Anzahl der verbleibenden Sicherungen in meinem Auftrag hinterlegen können.
  18. Ich möchte eine inkrementelle Sicherung erstellen, bevor die nächste Vollsicherung gemacht wird.
  19. Ich möchte die Anzahl der inkrementellen Sicherungen einstellen und mit meinem Auftrag speichern.

Klingt erstmal überschaubar, aber trotzdem steckt da eine ganze Menge Arbeit dahinter.

Ich versuche mich ja – auch wenn ich einen iPod benutze – mich von den Statistiken und Informationen immer mehr unabhängig von iTunes bzw. von last.fm zu machen. Ein Feature, was ich an iTunes mag, ist die Information, wann welcher Titel gespielt wurde. So kann ich nachsehen, welche Scheibe ich lange nicht mehr gehört habe und mal wieder reinhören.

Mein Vorteil ist, dass ich in meiner Plattenverwaltung die Chartinformationen zurück bis 2004 vorliegen habe. Das heißt ich habe eine Datenbasis, die wesentlich umfangreicher ist, als die von iTunes. Also kann ich doch Nutzen daraus ziehen. Meine Herausforderung ist lediglich die, dass ich die Informationen in zwei Stufen vorliegen habe.

  1. Ich habe seit Oktober meine Charts, wo ich mir in einer separaten Tabelle zu jedem Tonträger das Datum gemerkt habe, an dem die Platte zuletzt gespielt wurde. Ganz einfache Abfrage – alle Tonträger ermitteln und dann nachschauen, wann das letzte Datum zu der jeweiligen Platte ist.
  2. Meine Jahrescharts – dort liegen die Informationen pro Tonträger, wie oft ich die Platte gespielt habe. Wie gesagt, diese Informationen reichen bis in das Jahr 2004 zurück. Auch hier ist die Abfrage einfach: Pro Tonträger den letzten Jahreseintrag heraussuchen ggf. in Betracht ziehen. Einziger Knackpunkt hier ist, dass ich beim Einlesen vielleicht den einen oder anderen Tonträger nicht bei den Charts zuordnen konnte und somit kein Eintrag in meiner SQL-Datenbank entstanden ist. Das hat zur Folge, dass die Notstufe greifen muss.
  3. Die Ausnahme: Die Platte muss vor 2004 das letzte Mal abgespielt worden sein oder ich habe keinen Eintrag gefunden. Hier ist auch Platz für potenziellen Unfug: Der Tonträger ist z.B. 2008 erschienen, ich konnte ihn nicht zuordnen und deshalb steht als letztes Abspieldatum 2003 drin. Gibt es aber zum Glück nicht.

Kaum hatte ich das Feature implementiert und starte das erste Mal, musste ich zwei Erkenntnisse hinnehmen. Zum einen das erwartete „Stimmt, die hast du lange nicht mehr gehört“ und aber auch „Ich dachte, die Platte hätte ich verkauft?!“.

Heute geht es um Zweige und Quellcodeverwaltung. Der mitlesende Entwickler wird sich jetzt entspannt zurück lehnen und Bescheid wissen. Aber vielleicht ist es auch für Nicht-Entwickler mal interessant, wie so ein Entwickler es schafft, stabile Programme zu schreiben. Fangen wir mal mit der Quellcodeverwaltung an.

Falls uns mal die Bude abfackelt, lieben wir Entwickler es, wenn wir unseren Quellcode irgendwo ablegen können. Mit „abfackeln“ meine ich das weniger im wörtlichen Sinne, sondern auch wenn wir merken, dass wir uns beim Entwickeln verrannt haben und zu einem sicheren Punkt zurück kehren müssen, wo wir wussten, dass die Version noch lief. Was passiert dann in so einer Quellcodeverwaltung? Es gibt immer das Original auf dem Server und eine lokale Kopie. Sobald wir die lokale Kopie verändern, wird das von der Quellcodeverwaltung registriert und die Datei wird als „In Bearbeitung“ markiert. Ist man fertig mit seinen Änderungen, übergibt man der Quellcodeverwaltung seine Änderungen, wir sprechen dabei vom „Einchecken“. Oder man stellt fest, dass der Ansatz schlecht war und verwirft seine Änderungen und stellt den Originalzustand wieder her.

Wenn das schon zu kompliziert war, kommt jetzt der Teil, wo ihr komplett aussteigt. Stellt euch vor, dass viele Entwickler an dem Quellcode arbeiten, dass es immer möglich sein muss, stabile Versionen auszuliefern und trotzdem neue Features dazu kommen sollen. Schauen wir doch also mal, wie ein Feature in eine Version kommt…

Auf der Grafik sieht man, dass parallel mehrere Versionsstränge existieren, als Entwickler reden wir von sogenannten Zweigen oder Branches. Wenn mir also eine neue Idee kommt, wie z.B. den Musikstil zu einer Platte zu hinterlegen, dann lege ich einen neuen Zweig an. Jetzt beginne ich in kleinen Häppchen das neue Feature zu entwickeln. Datenbankänderung, Schnittstelle zum Lesen / Schreiben erweitern, Daten lesen / schreiben, Oberfläche erweitern und alles verknüpfen – jede von diesen Aktionen ist unabhängig von einander und kann somit einzeln eingecheckt werden (das sind die kleinen lila Punkte).

branches

Wenn mir jetzt mittendrin noch ein neues Feature einfällt, dann erzeuge ich einen neuen Zweig, der auch auf der Develop-Version basiert. Da mache ich auch wieder ein paar Änderungen. Und beide Features werden unabhängig von einander entwickelt und ich kann sie beliebig lange offen halten, ohne irgend etwas zu gefährden. Jetzt bin ich mit dem zweiten Feature zuerst fertig – kein Problem, es wird in die Develop-Version übernommen und kann jetzt nochmal auf Herz und Nieren getestet werden.

Ich mache es immer so: Wenn ich ein neues Feature entwickle und teste, starte ich die Plattenverwaltung immer aus der Entwicklungsumgebung heraus. Sollte ein Absturz auftreten, bleibe ich sofort an der entsprechenden Stelle im Quellcode hängen und kann sie korrigieren. Läuft das neue Feature stabil, baue ich eine Version und arbeite ganz normal damit. Knallt es jetzt nochmal, dann ziehe ich das Verhalten in der Entwicklungsumgebung nach und korrigiere den Fehler in der Develop-Version. Ist dann auch hier alles in Ordnung, kommt jetzt das Feature von der Develop-Version in die Master-Version. Hier kommt wirklich nur das stabile Zeug rein. Dort wird auch nichts mehr geändert.

Was, wenn jetzt das nächste Feature fertig wird? Unter Umständen habe ich durch die zwei Features Änderungen an den selben Stellen im Quellcode gemacht und die passen nicht mehr zusammen. Damit würde der Develop-Zweig instabil werden. Ganz schlecht, denn hier sollen nur kleine Bugs gefixt werden. Also passiert was, dass wir „Forward Integration“ nennen. Dafür gibt es kein schönes deutsches Wort. Es bedeutet, dass ich den Stand der Develop-Version (also mit Feature 2) in die Feature-Version übernehme. Jetzt wird alles korrigiert und stabilisiert, bis es passt und dann kann auch das neue Feature in die Develop-Version.

Ist ein Feature abgeschlossen, wird der entsprechende Zweig gelöscht, sobald er in der Develop-Version aufgegangen ist. Nur die Develop- und die Master-Zweige bleiben bestehen.

Auslöser – so könnte man sagen – ist das Diagramm von XKCD. Es zeigt, wann es sich lohnt, etwas zu automatisieren und wie viel Zeit man investieren sollte. Ich habe mal so als Richtwert 10 Minuten pro Monat genommen, die ich in die Eingabe meiner neuen Platten investiere. Laut Diagramm hätte ich also 10 Stunden investieren können, um eine automatisierte Anpassung vorzunehmen.

Da ich meine Musikdateien ohnehin nur noch in digitaler Form erwerbe, war mein Ansatz, dass ich eine Programmerweiterung schreibe, die über die Dateien drüber geht, die Tags aus der Datei ausliest und für die Plattenverwaltung erfasst. Also hab ich erstmal nach einer sinnvollen Bibliothek gesucht, die mir eine gute Funktionalität bietet und bin auf TagLib gestoßen. Was jetzt nur noch gemacht werden musste, war heraus zu finden, wie man an die gewünschten Daten kommt, dass entsprechende Verzeichnis auszuwählen, die Dateien ein zu lesen und die passende Datenstruktur zu füllen.

Nach knapp 3 Stunden hatte ich das Ergebnis in der Hand – alle Titel waren da und auch ein paar Daten bezüglich des gesamten Tonträgers. Ich freue mich schon, wenn ich jetzt eine Compilation kaufe, die 30 Titel hat und statt 10 Minuten verzweifelt Copy & Paste zu spielen, ist die Liste in 5 Sekunden eingelesen.

Klick auf den Button, Verzeichnis suchen und schon sind 13 Titel eingelesen

Klick auf den Button, Verzeichnis suchen und schon sind 13 Titel eingelesen

chartsVon Zeit zu Zeit kam ich dazu, noch etwas Feintuning an meiner Statistik vorzunehmen. Es kamen viele Kleinigkeiten zusammen…

  • Zum einen stellte ich von einem einfachen Liniendiagramm auf ein Flächendiagramm um. Sieht schon mal schöner aus.
  • Nachdem ich eine Compilation mit 100 Titeln abspielte und noch 4-5 Alben kam ich auf eine Abspielzeit von 36 Stunden. Das sieht in einer Tagesstatistik schon ziemlich merkwürdig aus. Also ersetzte ich die Tagesstatistik mit einer Jahresstatistik. Die ist zwar noch nicht aussagekräftig, aber wenn das Programm jetzt wieder 10+ Jahre läuft, dann kommt schon was dabei rum.
  • Dritter und letzter Punkt ist die Achsenbeschriftung. Lange hab ich damit herumgekämpft, jetzt habe ich es geschafft eine schöne Formatierung einzustellen, die sich entsprechend des eingestellten Wertes verhält.