Crow's NestMQTT

Crow’s NestMQTT and the Vibe Engineering Adventure

In the world of (Industrial) Internet of Things (IIoT), MQTT is a widely adopted messaging protocol. Whether you’re debugging a flaky device or trying to understand system-wide message flow, a good MQTT client is a lifesaver.

There are many excellent MQTT clients available—like MqttExplorer, MQTTX, mqttui, and the classic mosquitto_sub. However, none quite met my specific needs. That kicked off a wild journey: building my own client, Crow’s NestMQTT, while exploring how Generative AI can accelerate software development.


Running OPC UA server simulation in dotnet aspire

Simulating an OPC UA Server with .NET Aspire and OPC PLC

Deploying an OPC UA server simulation is a common need during development and testing of industrial IoT applications. Recently, a customer asked how to set up such a simulation using .NET Aspire, in order to streamline development workflows and easily monitor system components, logs, metrics, and inter-service communication.

.NET Aspire provides an ideal environment for orchestrating microservices and dependencies, making it a great fit for hosting a simulated OPC UA server. For the server simulation, I use the free and open-source OPC PLC provided by Microsoft. While it’s possible to run the server from source, I prefer using the containerized version published on the Microsoft Container Registry (MCR), which integrates more easily into an Aspire-based solution.


Zeit-Trennzeichen bei DateTime

Diese Woche hat es das .NET geschafft mich zu überraschen, ein Programm ist beim Kunden mit italienischen Windows immer wieder abgestürzt. Nach langem Suchen hat ein Kollege das Problem erkannt, was durch das folgende Beispiel veranschaulicht wird:

var ci = new CultureInfo("it-IT");
var dateTime = DateTime.Now;
var str = dateTime.ToString("dd.mm.yyyy hh:mm:ss", ci);
Console.WriteLine(str);

Ausgabe (.NET 3.5): 14.07.2016 20.45.30
Ausgabe (.NET 4.0): 14.07.2016 20:45:30

In der Ausgabe erkennt man, dass abhängig von der .NET Version, das Zeit-Trennzeichen ändert. Aber warum wird das Trennzeichen überhaupt verändert, es wurde doch eine feste Format-Zeichenkette angegeben? Die Dokumentation beschreibt:
The “d”, “f”, “F”, “g”, “h”, “H”, “K”, “m”, “M”, “s”, “t”, “y”, “z”, ":", or “/” characters in a format string are interpreted as custom format specifiers rather than as literal characters. To prevent a character from being interpreted as a format specifier, you can precede it with a backslash (\), which is the escape character.


Performance Vortrag aus 2015

Letztes Jahr habe ich einen Vortrag zum Thema Performance gehalten, der Vermitteln sollte warum dieses Thema jeden (.NET-)Entwickler betrifft.

Nachdem ich mir jetzt die Zeit genommen habe um auch die Sprechernotizen aka “Tonspur” auf zuschreiben, konnte ich den Vortrag online stellen:

https://github.com/koepalex/performance_talk_2015

Vielleicht ist der Vortrag für jemanden Hilfreich :)


Debugging von Performance Problemen in .NET

Die Analyse von Speicherproblemen ist eine Aufgabe die bei großen .NET Anwendungen häufiger vorkommt. In C++ wurde gesucht, wer welche Speicherblöcke angefordert und nicht wieder freigegeben hat und im .NET Umfeld wird eben gesucht warum der Gabarge-Collector den Speicher nicht freigeben kann. Oder man sucht warum einige Benutzeraktionen besonders lange benötigen. Für die Analyse gibt es eine ganze Reihe guter kommerzieller Programme, auf diese möchte ich jedoch nicht eingehen, sondern ein paar kostenlosen Alternativen vorstellen.


Neues vom GC in .NET 4.0 und 4.5

Heute möchte ich etwas über Erneuerungen im .NET 4.0 / 4.5 sprechen. Das genannte bezieht sich auf den Workstation Garbage-Collector. Die Informationen für den Server Garbage-Collector entnehmen sie bitte den Links. Beginnen wir jedoch zunächst mit einer kurzen Auffrischung.

Wiederholung

Das .NET Framework unterteilt seinen Heap in verschiedene Generationen.

  • In der Gen0 werden fast alle Objekte erstellt. Die Anfangsgröße beträgt rund 256KB.
  • In der Gen1 werden Objekte gespeichert die eine Garbage-Collection überlebt haben. Die Anfangsgröße beträgt rund 2 MB.
  • In der Gen2 werden Objekte gespeichert die mehr als eine Garbage-Collection überlebt haben Die Anfangsgröße beträgt 10 MB.

Die soeben genannten Anfangsgrößen können zur Laufzeit variiert werden. Am stärksten wird sich i.A. die Größe der Gen2 verändern (wachsen), da die Gen2 als Endlager für alle länger benötigten Objekte dient.


GetHashCode dein Freund und Sorgenkind

Eine wichtige Methode beim Arbeiten im .NET Umfeld ist GetHashCode. Sie gibt einen 32 Bit Integer zurück der das Objekt identifizieren soll. Der Hash-Code beschreibt also die Identität des Objektes (im Gegensatz zur Speicherreferenz auf das Objekt).

Daraus leitet sich die Frage ab Wann zwei Objekte die selbe Identität besitzen? Im Falle einer Object-Relational-Mapper Klasse beispielsweise, wenn die Instanzen der O/R-Mapper Klasse auf ein und die selbe Zeile(n) der selben Tabelle(n) der selben Datenbank(en) verweisen. Klassischer weise durch eine Kette von Primary-Keys. Der Hash-Code ist auch ein Sorgenkind eines Entwicklers da schon hier die Probleme beginnen. Eine einfach Implementierungen könnte sein:


Pipe aus eigenen Programmen nutzen

Mittels der von Unix Systemen bekannten Pipe (|) ist es auch unter Windows (via CMD-Line) möglich einzelne Kommandos zu verbinden. Die Pipe ermöglicht es die Ausgabe vom dem vorherigen Kommando direkt als Input des aktuellen Kommandos zu verwenden. Beispielsweise den Inhalt einer Datei einem Skript zu übergeben: type input.md | perl Markdown.pl > output.html

Logisch gesehen Ersetzt der Inhalt der Pipe ein einzelnes Konsolen-Argument. Um diese Funktionalität auch in den eigenen (Konsolen-) Programme verwenden zu können, sind im Allgemeinen nur zwei Erweiterungen notwendig.


Ermitteln des PID (Processidentifier) des Vaterprozesses

In manchen Fällen ist es notwendig heraus zu finden, welches der Vaterprozess eines Prozesses ist. Dafür gibt es im Allgemeinen drei verschiedene Lösungen im Windows .NET Umfeld.

Die häufigste Lösung ist die Verwendung von Performancecountern.

var process = FindProcess();  
using (var pC = new PerformanceCounter(
    "Process",  
    "Creating Process ID",  
    string.Format("{0}#{1}", process.ProcessName, 1),    
    process.MachineName))  
{  
    int pid = (int)pC.NextValue();  
    Console.WriteLine("parent pid = {0}", pid);  
}

Die Verwendung von Performancecountern im Allgemeinen kann zwei mögliche Nachteile haben:


DSLs, Extension Methods und Fluent Interfaces

In den letzten Jahren wurde in der Softwareentwicklung viel über Domänenspezifische Sprachen (domain specific language, kurz: DSLs) geschrieben. DSLs sollen von einem Domänenexperten gelesen werden können, auch ohne das die Domänenexperten über Programmierkenntnisse verfügen. DSLs können auch Entwicklern helfen die Quelltexte leserlicher zu machen, sowie Fehler zu vermeiden. Die Quelltextzeile:

if (!SessionEstablished)

Ist einfach verständlich (für diejenigen welche eine C ähnliche Syntax verstehen), als Alternative könnte folgende Quelltextzeile dienen: