EPPLUS - Excel Library für C# / .NET

Falls ihr einmal in die Verlegenheit kommt und ein Excelsheet über .NET erstellen müsst, könnt ihr natürlich ganz einfach das COM-Objekt einbinden und damit auf alle möglichen Objekte zugreifen. Doch spätestens nach dem Entladen des COM-Objektes werdet ihr euch irgendwann wieder die Haare raufen. Da ich sowieso nicht mehr soviele auf dem Kopf habe, ist es ja nicht so schlimm. Lieber eine Glatze, als gar keine Haare mehr.

Doch nach ein bisschen Googlen nach Lösungen bin ich auf diese Library gefunden. Und was soll ich sagen: Sie funktioniert. Erstens sehr schnell, zweitens sehr elegant, durch wenige Aufraufe und kompakte Schreibweisen und drittens werden keinerlei Excel Objekte erzeugt, die nach dem Beenden des Programms für Probleme sorgen könnten.

EPPLUS ist eine sehr schöne Library um aus C# / .NET und dazu noch Open Source. Und für die ganz Faulen wie mir gibt es das Ganze sogar als Nuget Paket. Bei soviel Service habe ich dem Projekt auch mal eine kleine Spende zukommen lassen. Schließlich leben Programmierer ja nicht nur Luft und Code, sondern auch von Kaffee, Tee, Mate oder auch mal ein Bier.

Dieser Beitrag ist auch als Gastbeitrag bei daMax erschienen.

The Perl Jam 1 und 2

Weil mir jemand eine Mail zu einem sehr alten Beitrag mit einem Link zu einer Perl Seite geschickt hat, eben noch dieser Beitrag zu den Perl Jam Videos.

Auf dem Congress wurden 2014 und 2015 zwei lustige Talks gehalten:

The Perl Jam

The Perl Jam 2 - The Camel Strikes Back

Ich bin ja nun kein Perl Entwickler. Mich hätte es damals nur gereizt um selbst mal solchen Code zu fabrizieren. Aber nachdem ich nun selbst lange Jahre als Admin arbeite, hätte ich dann doch auch meine Bedenken diesen Code dann auch mal auszuführen. Da kann man ja gar nicht mehr erkennen, was der Code eigentlich macht. Löscht er die Festplatte? Schickt er ein Selfie von der Webcam zu Facebook oder schreibt er einfach nur "Wer das ausführt ist doof!" in die Shell.

Andererseits bin ich ja nicht nur leidgeprüfter Windows Admin, sondern auch ein mehr oder weniger erfahrener Programmierer. Angefangen hat alles mal mit einem Programm in VB.NET. Das sollte nur so einfache Dinge machen, wie den Lagerbestand prüfen, schauen ob andere Aufträge vorher den Bestand aufbrauchen und wenn es dann immer noch lieferbar ist, schick eine Mail raus. Ganz einfach nur nur 1300 Zeilen herrlichen VB.NET Code. Ganz ohne Objekt Orientierung.
Wir haben damals in der Berufschule eben noch C gelernt und als es dann um Objekt Orientierung ging, war die Ausbildung auch schon zu Ende. Dafür habe ich die Grundsätze wie Schleifen, If-Else, Switch und Variablen Zuweisen trotzdem gut in VB.NET übernehmen können. Wenn ich das Programm heute nochmal frisch schreiben würde, wäre der Code deutlich auf mehr Dateien verteilt und vor allem in Klassen aufgeteilt. Denn manche von diesen 1300 Zeilen sind auch doppelt, weil sie in manchen Funktionen eben doppelt vorkommen.
Tja viele Jahre später ist man eben schlauer.

Aber was bei bei Perl so abgeht, dass die Funktionen eben komplett andere Werte ausgeben als es üblich ist macht die Sache nicht besser. Genau deshalb rate ich eben jedem Programmieranfänger erstmal von Perl ab. Zumindest Perl 5 auf die sich die Talks beziehen.

Andererseits ist Perl auch toll, weil es sowas wie Regex hervorgebracht hat. Das hab ich damals anno 2010 auch mal probiert, aber leider (oder vielleicht auch glücklicherweise? ) zum Einsatz bringen können.

Wer Programmieren lernen will schnappt sich eben ein Buch und lernt Python. Das kann man später auch noch gut gebrauchen. Wer was blinken sehen will, schnappt sich eben einen Arduino und lernt ein bisschen C dabei.

Oder macht es wie ich: In der Schule C lernen, dann auf der Arbeit mit VB.NET weitermachen und später ein Buch über C# holen und endlich die Objekt Orientierung lernen. Klassen und Methoden sind toll und ich freue mich, dass ich es endlich mal kapiere!
Für Arduino muss man sich davon aber wieder verabschieden, da ein Mikrocontroller eben nur 32kb zur Verfügung hat. Was aber auch erstmal reicht für den Anfang.

Und zu Perl: Wenn ihr es nicht unbedingt braucht, lasst es! Fürs Web Backend nimmt man heute eher PHP (naja auch eine lausige Sprache und mit jedem Release sind alte Versionen inkompatibel) oder JavaScript (jo ist mir auch irgendwie zu komplex). Aber Perl hab ich bei mir noch nie gesehen!

Trolling Level C#

Bei daMax hab ich eine wirklich böse Art des Trollens eines Programmierers entdeckt.
Es ist tatsächlich so, dass Semikolon und das griechische Fragezeichen sind zwei unterschiedliche Zeichen im Unicode. Da war doch schon bei der Festlegung des Unicodes ein Troll dabei. Denn meiner Meinung hätte man beide Zeichen einfach zusammenfassen können. Ist ja eh das Gleiche. :)

trolling-c#

Aber wahrscheinlich gibt es dafür natürlich einen sehr wichtigen Grund, warum das griechische Fragezeichen und das Semikolon nicht ein und dasselbe Zeichen sind. :D

Mit der Klinge durch den Kopf in die Brust

Gerade bin ich wieder dabei ein bisschen zu programmieren. Aktuell eigentlich nur eine ganz einfache Verarbeitungen von CSV Dateien. Wenn dort etwas drin steht, muss ich eben die eine Datenbank Abfrage machen und Daten ändern.
Wenn aber bereits Datensätze vorhanden sind, die auch als fertig markiert sind muss ich eben einen neuen Datensatz erstellen.

Früher hab ich diese ganzen Dinge ja alle ohne wirklich Struktur gemacht. Da mal eine Funktion, da mal ein Sub. All das was man so als C-Anfänger kennen gelernt hat und in Visual Basic.NET recht einfach ging.
Nun wurde es aber Zeit, dass ich mal Objekt Orientierung lerne. Deshalb hab ich mir C# angeschaut und erstmal ein Buch gelesen. Während dem Lesen wurde vieles klar, was ich in meinen letzten Programmierprojekten alles besser hätte machen können. Die Vererbung und Interfaces sind echt nette Sprachkonzepte. Die Lambdas sind auch sehr schöner Syntax Zucker. Als ich dann so die Grundlagen mal alle durch hatte und nur noch der grafische Teil kam hab ich das Buch beiseite gelegt und ein paar Wochen andere Sachen gemacht.
Diese Woche hab ich dann wieder angefangen und ein kleines Beispiel mit schon netten Wrappern für COM-Funktionen genommen und darauf mein Programm geschrieben und dabei die Wrapper ein bisschen erweitert. Aber irgendwie war es auch wieder recht kompliziert. Plötzlich muss ich ja Klassen instanzieren, kann die gerade aktive COM-Verbindungen nicht klassenübergreifend verwenden.
Doch das geht natürlich. Das Zauberwort heißt hier: Statische Klassen mit dem Basis Objekt der COM-Verbindung. Wenn ich nun dieses statische Objekt aus der statischen Klasse nehme, kann ich von jeder x-beliebigen Klasse darauf zugreifen und damit alle Dinge machen. Schon sehr geil. Früher hatte extra wegen diesem Basis Objekt, alles in einer Datei geschrieben. Ich hatte aber auch noch keinen Schimmer von Namespaces, die mir erlauben einzelne Funktionen innerhalb des gleichen Namespaces auf verschiedene Dateien zu verteilen.

Letzendlich scheitere ich aber dann doch wieder an der COM-Verbindung. Die wirft mir wieder undokumentierte Fehler, verwirrt mich und was immer ich versuche zur Lösung beizutragen: Es wird einfach ignoriert. In der Doku sieht alles so logisch aus. In der Realität finde ich genau ein spezielles Feld, welches mir alles kaputt macht. Laut Datenbank ist es ein Integer, aber im Programm wird es als Dropdown mit eindeutigen Strings dargestellt!

Deshalb: Mit der Klinge durch den Kopf in die Brust!

Das klingt verwirrend? Ist es auch und beschreibt meinen aktuellen Zustand: Verwirrung!

Einen (einfachen) Würfelgenerator mit Arduino bauen

Für ein Baumhausprojekt soll ich mit einigen anderen Leuten den Pfadfinder Kids etwas von Mikrocontrollern beibringen. Dazu machen wir natürlich erstmal die Standardbeispiele. LED ein und ausschalten, eine kleine Ampel und andere Dinge.
Da ich mich mit einem anderen ITler zusammen gehockt habe um etwas vorzubereiten war uns langweilig und wir wollten mal etwas eigenständiges entwickeln. Die 7 Segment Anzeige kennt ja jeder, der noch eine alten Radiowecker hat. Das tolle dabei: Man kann jeden einzelnen Strich ansteuern. Also haben wir das einfach mal gemacht und wollten einen Würfel bauen, der per Knopfdruck "zufällig" eine Zahl zwischen 1 und 6 anzeigt. Quasi die Nerd Variante für Leute, die gerne Würfelspiele spielen. :)

Das Ganze hat uns anfangs erstmal Fragen an den Kopf geworfen. Als blutiger Anfänger im Arduino Bereich könnte man ja denken, dass einfach alles in setup() und loop() untergebracht werden. Also definieren für Setup einen Anfangszustand und im loop dann die Ansteuerung vom Würfel und den Zahlen. Das fiese ist: Alles was in loop() steht wird immer von oben bis unten ausgeführt. Wenn ich dort also den Zustand des Druckknopfes abfrage wird danach trotzdem die Zufallsfunktion abgefragt. Deshalb hat der Button keine Bedeutung mehr.

Also lagern wir die Switch Anweisung mit den ganzen Zahlen in einen eigene Funktion aus und rufen diese dann nur noch auf loop() aus. Wenn man dann noch den Zustand vom Button richtig abfrägt (also beispielsweise anfangs mal den Zustand mit Seriel.println() ausliest) kann die if() Funktion auch greifen.
Unter Arduino gibt es nicht wirklich guten Zufall. Es lohnt sich aber den Zufall mit randomSeed() pseudomäßig zu verbessern. Dann kommt auch mal statt immer nur 1, 2, 3, 5 auch mal 4, 6 zum Vorschein. Mit random() wird dann der Zufall erzeugt.

Das Ganze funktioniert relativ gut. Wenn man noch die Muse hätte und ein kleines Gehäuse und einen kleinen Teensy oder arduino mini verwenden würde, hätte man einen elektronischen Würfel. Das wäre ein netter Gag für zwischendurch.

Die Sprache für Arduino ist ja hauptsächlich C und C++. Das hab ich das letzte Mal in der Berufschule gelernt und programmiert. Aber dieser Code ist mir dann doch relativ leicht gefallen. Natürlich ist es immer sinnvoll auch ein bisschen Literatur über die Bauteile zu haben. Anfangs haben wir es zusammen ausprobiert, später hab ich es alleine noch mit den Knopf hinprobiert und den Code optimiert, damit er besseren Zufall generiert und auch wirklich alle 6 Zahlen anzeigen kann. Das random(1,6) hat nämlich nicht funktioniert. Dann nimmt er nur Zahlen von 1 bis 5. Deshalb muss immer max+1 sein. Also in diesem Fall 7.
Es ist eine gute Kombination aus Anfängerbeispielen (Druckknöpfe, Werte auslesen, 7 Segment Anzeige). Und man muss etwas Verständnis von Programmiersprachen mitbringen. Ich hab in den letzten Jahren hauptsächlich VB.NET Programme geschrieben. Aber auch dort ist es ja ähnlich. Man hat Funktionen die man mit Inhalt füttern kannn und bekommt hinterher wieder etwas zurück. Der einzige Unterschied zwischen Rechnern und dem Arduino ist: Das Fehlen eines grafischen User Interfaces. Doch das macht genau den Reiz aus, man kann wieder neue Dinge überlegen. Eine LED als Status oder ein Button als Eingabe Taste. Und man muss wieder diese ; setzen, sonst meckert der Compiler. äöü in Variablen Namen mag der Compiler auch nicht. Das hab ich erst gestern wieder gemerkt, als wir die Ampelschaltung nachgebaut haben. :) Ich hätte es ja noch wissen können, aber es macht einfach Spaß Fehler zu machen und dann hinterher die Hand an die Stirn zu klatschen!

Anbei noch der Code mit Kommentaren zum Nachbauen.

//Programm um per Knopfdruck eine Zahl zwischen 1 und 6 zu bekommen.
//Die Zahl muss explizit an eine eigene Funktion übergeben werden.
//Andernfalls läuft der Controller andauernd alle Zahlen durch und ignoriert
//den Zustand des Buttons.

//Ersteller: Daniel Pfeil und Tobias Aichele
//Datum: 31.01.2015

//Button muss auf GND und 5V angeschlossen werden.
//Zwischen Button und 5V muss ein 10k Widerstand rein.
//Button auf Pin 1

//7Segment Anzeige
//oben (links nach rechts)
// 10 9 8 7 6
// Pin 8 mit 220 Widerstand auf GND
// 7Segment Pin --> Arduino
// 10 --> 11
// 9 --> 10
// 7 --> 8
// 6 --> 7
//
// unten (links nach rechts)
// 1 2 3 4 5
// Pin 3 bleibt leer
// 7Segment Pin --> Arduino
// 1 --> 2
// 2 --> 3
// 4 --> 5
// 5 --> 6

//Pins der 7 Segment Anzeige
int a=8;
int b=7;
int c=5;
int d=3;
int e=2;
int f=10;
int g=11;
int dp=6;

//Pin vom Druckknopf
int pinButton=1;

void randomNumber(int number)
{
  switch (number)
  {
    case 1:
    digitalWrite(a,LOW);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,LOW);
    digitalWrite(e,LOW);
    digitalWrite(f,LOW);
    digitalWrite(g,LOW);
    digitalWrite(dp,LOW);
    break;
       
    case 2:
    digitalWrite(a,HIGH);
    digitalWrite(b,HIGH);
    digitalWrite(c,LOW);
    digitalWrite(d,HIGH);
    digitalWrite(e,HIGH);
    digitalWrite(f,LOW);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
    break;
       
    case 3:
    digitalWrite(a,HIGH);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e,LOW);
    digitalWrite(f,LOW);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
    break;
       
    case 4:
    digitalWrite(a,LOW);
    digitalWrite(b,HIGH);
    digitalWrite(c,HIGH);
    digitalWrite(d,LOW);
    digitalWrite(e,LOW);
    digitalWrite(f,HIGH);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
    break;
       
    case 5:
    digitalWrite(a,HIGH);
    digitalWrite(b,LOW);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e,LOW);
    digitalWrite(f,HIGH);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
    break;
       
    case 6:
    digitalWrite(a,HIGH);
    digitalWrite(b,LOW);
    digitalWrite(c,HIGH);
    digitalWrite(d,HIGH);
    digitalWrite(e,HIGH);
    digitalWrite(f,HIGH);
    digitalWrite(g,HIGH);
    digitalWrite(dp,LOW);
    break;
  } //Ende switch
} //Ende setup()

void setup()
{
  digitalWrite(a,LOW);
  digitalWrite(b,LOW);
  digitalWrite(c,HIGH);
  digitalWrite(d,LOW);
  digitalWrite(e,HIGH);
  digitalWrite(f,LOW);
  digitalWrite(g,LOW);
  digitalWrite(dp,LOW);
  pinMode(pinButton, INPUT);
  randomSeed(analogRead(0)); //Anlog Pin 0 ist nicht angeschlossen
  //Generiert etwas besseren Zufall
}

//Variablen für loop()
int zufall;
int buttonstate = 0;

void loop()
{
  buttonstate = digitalRead(pinButton);
 // Serial.println(buttonstate);

    if (buttonstate == LOW)
    { 
    zufall = random (1,7); //min: 1 ; max: 7-1 =6
    randomNumber(zufall);
    delay(500);
    }
} //Ende loop
//Programm fertig.

//Falls man alle 6 Zahlen hintereinander ausgeben möchte:
//  //Gebe alle Zahlen von 1 bis 6 aus.
//Delay macht Sinn um eine kurze Pause zwischen den einzelnen Zahlen zu haben.
//
//delay(500);
//  
////   case 1:
//digitalWrite(a,LOW);
//digitalWrite(b,HIGH);
//digitalWrite(c,HIGH);
//digitalWrite(d,LOW);
//digitalWrite(e,LOW);
//digitalWrite(f,LOW);
//digitalWrite(g,LOW);
//digitalWrite(dp,LOW);
//
//delay(500);
//
////   case 2:
//digitalWrite(a,HIGH);
//digitalWrite(b,HIGH);
//digitalWrite(c,LOW);
//digitalWrite(d,HIGH);
//digitalWrite(e,HIGH);
//digitalWrite(f,LOW);
//digitalWrite(g,HIGH);
//digitalWrite(dp,LOW);
//
//delay(500);
//
////   case 3:
//digitalWrite(a,HIGH);
//digitalWrite(b,HIGH);
//digitalWrite(c,HIGH);
//digitalWrite(d,HIGH);
//digitalWrite(e,LOW);
//digitalWrite(f,LOW);
//digitalWrite(g,HIGH);
//digitalWrite(dp,LOW);
//
//delay(500);
//
////   case 4:
//digitalWrite(a,LOW);
//digitalWrite(b,HIGH);
//digitalWrite(c,HIGH);
//digitalWrite(d,LOW);
//digitalWrite(e,LOW);
//digitalWrite(f,HIGH);
//digitalWrite(g,HIGH);
//digitalWrite(dp,LOW);
//
//delay(500);
//
////   case 5:
//digitalWrite(a,HIGH);
//digitalWrite(b,LOW);
//digitalWrite(c,HIGH);
//digitalWrite(d,HIGH);
//digitalWrite(e,LOW);
//digitalWrite(f,HIGH);
//digitalWrite(g,HIGH);
//digitalWrite(dp,LOW);
//
//delay(500);
//
////   case 6:
//digitalWrite(a,HIGH);
//digitalWrite(b,LOW);
//digitalWrite(c,HIGH);
//digitalWrite(d,HIGH);
//digitalWrite(e,HIGH);
//digitalWrite(f,HIGH);
//digitalWrite(g,HIGH);
//digitalWrite(dp,LOW);
//  
//delay(2000);