INT zu BCD Algorithmus

In der Ausbildung kamen wir an einen Punkt, wo wir einen Zahlenwert in die BCD-Schreibweise überführen mussten. Bevor ich erfahren habe, dass die SPS-Software einen vorgefertigten Baustein hat, habe ich mir selber einen Algorithmus ausgedacht.

Der Algorithmus für Zahlen von 0-99 ist wie folgt aufgebaut:

\text{I} = \text{int}(\frac{x}{10})

\text{II} = x - (\text{I} \cdot 10)

\text{III} = (\text{I} << 4)

\text{IV} = \text{II} + \text{III}

Als Rust-Programm:

fn int_to_bcd(x: i32) -> i32 {
    let tenths: i32;
    tenths = (x / 10) as i32;
    let ones: i32;
    ones = x - (tenths * 10);
    let shifted_tenths: i32;
    shifted_tenths = tenths << 4;
    shifted_tenths + ones
}

Als Funktionsblockdiagramm:

screenshot_20170111_180821

Werbeanzeigen

REST-CodeSchnipsel: RestRecipes

Vor kurzem ist bei mir die Notwendigkeit nach eine REST API aufgekommen und ich wollte keine großen Frameworks dafür nutzen, die viel zu viel Overhead mitliefern.

Aus dem Grund habe ich mir selber eine kleine Sammlung an Hilfsklassen erstellt, die mir das Leben einfacher machen. Die Sammlung habe ich RestRecipes getauft.

Zu finden unter: https://github.com/maikwoehl/rest-recipes

Nutzbar wie folgt:

require_once "RestRecipes/autoload.php";

use RestRecipes\Router;

$app = new Router();

$app->route("/order/<id>", "GET", function($id) {
    // Fancy stuff with $id
});

$app->route("/order/<id>", "PUT", function($id, $data) {
    // Fancy stuff with $id and $data
});

$app->route("/order/<id>", "POST", function($id, $data) {
    // Fancy stuff with $id and $data
});

$app->route("/order/<id>", "DELETE", function($id) {
    // Fancy stuff with $id
});

Das ganze kann auch per Composer im eigenen Projekt eingebunden werden:

~$ composer require maikwoehl/rest-recipes

Objektorientierung in PHP

Die  Web-Skriptsprache PHP ist an sich ja schon in einigen Kreisen verpönt, aber ich habe zugestimmt eine einfache Web-Shop Software für einen Freund zu programmieren.
Natürlich habe ich nicht vor xt:commerce nachzuprogrammieren, aber etwas kleines und feines sollte es schon sein. Also habe ich mich drangesetzt und mich in OOP mit PHP eingearbeitet und war erst beeindruckt und an einigen Stellen enttäuscht. Meine Erfahrungen möchte ich in diesem Beitrag teilen.

Interfaces

Interface heißt auf Deutsch Schnittstelle und ist das, was es bedeutet. Ein Interface bietet eine Schnittstelle zu einem Projekt und ihren Objekten. Entscheidet sich ein Programmierer dazu, solch ein Projekt zu modifizieren, dann kann der ein Interface implementieren und stellt damit sicher, dass das Projekt die gleichen Signaturen vorfindet.

Nicht nur ein anderer Programmierer, sondern auch man selber profitiert von Interfaces, weil diese Flüchtigkeitsfehler schnell aufdecken können.

Lange Rede, kurzer Sinn: Ein Interface wird wie folgt geschrieben:

interface iMyClass {
  public function sampleMethod();
  public function exampleMethod($someVariable);
}

Klassen

Klassen im einfachen Sinne sind Baupläne für Objekte. Auch hier ist PHP mittlerweile gut dabei, denn Type Hinting erlaubt es einem, bestimmte Variablen an Typen zu binden. Mehr dazu in der PHP-Dokumentation.

class MyClass implements iMyClass {
  public $someProperty;
  public array $someArrayProperty;
  public AnotherClass $someAnotherClassProperty;

  protected $someProtectedProperty;
  private $somePrivateProperty;

  public function __construct()
  {
    // Constructor
  }

  public function getSomeProperty()
  {
    return $this->someProperty;
  }

  public function setSomeProperty($someProperty)
  {
    $this->someProperty = $someProperty;
  }
}

Das ist ja alles schon ganz nett. Doch kommen wir jetzt zu den Ärgernissen.

Ärgernis 1: Überladung von Konstruktoren

Ab und zu kommt es vor, dass man Konstruktoren überladen möchte. In PHP ist es nicht möglich eine Methode innerhalb einer Klasse zweimal zu deklarieren. Dadurch kommt ein typisches Überladen, wie man es aus Java oder C# kennt, nicht infrage.

Vielmehr muss hier zu einem Trick gegriffen werden, der in der PHP-Dokumentation in einem Kommentar erklärt wird.

Ärgernis 2: Überladung von Methoden

Das Überladen von Methoden ist auch etwas umständliches in PHP. Hier muss zu sogenannten Magic Methods gegriffen werden. In diesem Fall werden diese über __call() aufgerufen.

class AnotherClass {
    public function __call($name, $arguments)
    {
        if ($name == "someFunction")
        {
              switch (count($arguments))
              {
                  case 0:
                      // Do something
                      break;

                  case 1:
                     // Do something different with one Argument
                     break;
              }
        }
    }
}

Das ist eine Möglichkeit an die Sache heranzugehen. Es gibt aber noch mehr. Welche man nutzt, liegt beim persönlichen Geschmack.

Fazit

Wenn ich nicht müsste, dann würde ich nicht in PHP programmieren. Wider Erwarten hat sich PHP in den letzten Jahren zu einer anderen Sprache entwickelt. Es ist für mich zu einer Skriptsprache geworden, die ich nicht mehr allzu sehr verabscheue. Auch wenn es wahrscheinlich ein Großteil an Erfahrung ist, der in diese Erkenntnis einfließt.

TypeScript Entwicklungsworkflow

Seit ein paar Tagen stelle ich meinen Entwicklungsworkflow für HTML5-Applikationen um. Ich nutze jetzt viel mehr TypeScript und möchte mir das Entwickeln damit natürlich erleichtern. Zum Glück bietet der TypeScript Compiler einige Features, die einem dabei helfen.

Zu aller erst:

~$ tsc --init

Mit diesem Befehl wird eine Datei mit dem Namen tsconfig.json angelegt. Nach dieser Datei sucht der TypeScript Compiler automatisch und führt den darin beschriebenen Kompiliervorgang aus.

~$ tsc -w

Dieser Parameter veranlasst tsc dazu, das Verzeichnis zu überwachen und bei jeder gespeicherten Änderung die Datei(en) neu zu kompilieren.

~$ tsc -d

TSC bietet außerdem die Möglichkeit aus dem Projekt Definitionen zu erzeugen, damit fremde Programmierer Statement Completion nutzen können, ohne die TypeScript-Datei zu laden.

OpenShift – Begrenzt kostenlose App Engine

Auf meinen Streifzügen durch das Internet bin ich auf OpenShift gestoßen. Dieser Service bietet als kostenlosen Service drei Applikationen mit 1GB Speicher pro Applikation an. Ich rede hier von http://openshift.com.

Unter den nutzbaren Engines findet man PHP, node.js und Python. Ich habe mir erlaubt, eine node.js-Applikation einzurichten, die auf http://testapp-maikw.rhcloud.com erreichbar ist. Zur Zeit sieht man dort noch nicht fiel, aber ich werde diesen Service unter anderem wahrscheinlich dazu nutzen, um meine Vertretungsplan-App mit einem CORS-Bypass auszustatten, der momentan über einen privaten Server läuft.

Wer also kostenlos mit ein paar Engines spielen möchte, dem sei OpenShift ans Herz gelegt. Wer über einen eigenen Webserver verfügt, der hat dadurch natürlich noch ganz andere Möglichkeiten.

Dennoch bietet OpenShift SSH-Zugang, Git Source Control und Unterstützung für viele Plattformen.

Netzwerkprotokoll programmieren #4

Heute habe ich das erste Mal die Bibliothek durch den Simulator von Atmel Studio 7 geschickt und mir sind einige Fehler in meiner Bibliothek aufgefallen. Hier eine grobe Übersicht:

  • index-Variable i (uint8_t) wurde runtergezählt und nach 0 kam 255
    • Die Kontrollabfrage durfte nicht i==255 lauten, sondern i < 255
  • sizeof() lieferte nicht die korrekte Länge des Arrays bei der Rückgabe der Adresse der ersten Speicherstelle (sizeof() gab 4 zurück)
    • strlen() aus der string.h löste das Problem (strlen() gab 101 zurück)
  • Die for-Schleife, die die Bits eines char’s des Textinhalts in das Paket-Array schreiben sollte, zählte einen zuviel und sprengte den Datenbereich des Pakets.

Bei der Analyse musste ich die I/O Ansicht und das Überwachungsfenster verwenden. Außerdem heftete ich mir die Laufvariablen im Editor an, um diese einfach überblicken zu können.


Nun zu der nächsten Funktion: Die BuildStringPackage()-Funktion.

char* BuildStringPackage(uint8_t dst, uint8_t src, char *data, size_t data_length)
{
	static char newPackage[PKG_C_SIZE + (PKG_S_SIZE * 8) + 4 + 1] = ""; // Standard-Größe von 17 plus Maximale Länge der Daten plus Länge des END_OF_TEXT Nibbles

	newPackage[0] = '1'; // START

	// Destination
	char dstArray[CMD_SIZE + 1] = "";
	SIConvertIntToBinaryString(dstArray, dst, sizeof(dstArray));
	newPackage[1] = *(dstArray + 0);
	newPackage[2] = *(dstArray + 1);
	newPackage[3] = *(dstArray + 2);
	newPackage[4] = *(dstArray + 3);

	// Source
	char srcArray[CMD_SIZE + 1] = "";
	SIConvertIntToBinaryString(srcArray, src, sizeof(srcArray));
	newPackage[5] = *(srcArray + 0);
	newPackage[6] = *(srcArray + 1);
	newPackage[7] = *(srcArray + 2);
	newPackage[8] = *(srcArray + 3);

	// Start of Text
	newPackage[9] =  '0';
	newPackage[10] = '0';
	newPackage[11] = '1';
	newPackage[12] = '0';

	if (data_length <= PKG_S_SIZE) // Ist die Länge der Daten kleiner oder gleich der erlaubten Maximallänge der Daten?
	{
		uint8_t i;
		for (i = 0; i <= data_length - 1; i++) // Durchgehen des Daten-Arrays | Stripping terminating zero
		{
			char characterArray[STR_SIZE + 1] = ""; // Länge eines Buchstabens + 1 (terminating zero)
			SIConvertIntToBinaryString(characterArray, *(data + i), sizeof(characterArray));

			for (uint8_t j = 0; j <= STR_SIZE; j++)
			{
				newPackage[12 + 1 + (i * 8) + j] = characterArray[j]; // Jedes Bit nacheinander in das Array schreiben
			}
		}

		while (i < PKG_S_SIZE) // Den Rest bis 10 Bytes voll sind, mit 0 auffüllen
		{
			char characterArray[STR_SIZE + 1] = "";
			SIConvertIntToBinaryString(characterArray, 127, sizeof(characterArray)); // 0x74 DEL

			for (uint8_t j = 0; j <= STR_SIZE; j++)
			{
				newPackage[12 + 1 + (i * 8) + j] = characterArray[j];
			}
			i++;
		}
	}

	// End of Text
	newPackage[12 + (PKG_S_SIZE * 8) + 1] = '0';
	newPackage[12 + (PKG_S_SIZE * 8) + 2] = '0';
	newPackage[12 + (PKG_S_SIZE * 8) + 3] = '1';
	newPackage[12 + (PKG_S_SIZE * 8) + 4] = '1';

	// End
	newPackage[12 + (PKG_S_SIZE * 8) + 4 + 1] = '0';
	newPackage[12 + (PKG_S_SIZE * 8) + 4 + 2] = '1';
	newPackage[12 + (PKG_S_SIZE * 8) + 4 + 3] = '0';
	newPackage[12 + (PKG_S_SIZE * 8) + 4 + 4] = '0';

	newPackage[12 + (PKG_S_SIZE * 8) + 4 + 4 + 1] = '\0'; // Terminating zero

	return newPackage;
}

Der erste Teil ist nahezu identisch mit der Funktion BuildPackage(), die mittlerweile BuildCommandPackage() heißt. Mittlerweile wurden auch einige Konstanten eingefügt, wodurch der Code etwas flexibler für die endgültige Definition des Protokolls wird.

  • PKG_C_SIZE = 17 | 17 Bits an Kommandos
  • PKG_S_SIZE = 10 | 10 Bytes an Daten (aktuell)
  • CMD_SIZE = 4 | Ein Kommando ist 4 Bit lang
  • STR_SIZE = 8 | Ein Buchstabe (char) ist 8 Bit lang

Mit ein wenig Mathematik kann die Funktion die entsprechenden Offsets zum Einfügen der Kommandos selber ermitteln. Ganz wichtig ist das beim Einfügen der Daten. Hier wird durch das Daten-Array durchiteriert und jeder Buchstabe wird in einen Binärstring umgewandelt und dann Bit für Bit von links nach rechts in den Paket-String eingearbeitet. Sollten Daten kleiner als die verfügbaren 10 Bytes sein, so wird der Rest mit einer 0 aufgefüllt (0x30 bzw. 48).

Ein Datenpaket kann wie folgt aussehen:

1|0001|0010|0011||0100|1000|0110|0001|0110|1100|0110|1100|0110|1111
|0111|1111|0111|1111|0111|1111|0111|1111|0111|1111||1001|0100

  • | markiert das Ende eines Nibbles
  • || markiert den Anfang und das Ende der Datenbytes

Wer Spaß an dem Entziffern von Binärzahlen hat, kann den Inhalt ja einmal entziffern. Die Daten sind im ASCII-Standard kodiert.
Die Lösung ist folgende:

0x1 - Start
0x1 - Zieladresse
0x2 - Absenderadresse
0x3 - Start der Daten
0x48 - H
0x61 - a
0x6C - l
0x6C - l
0x6F - o
0x7F - DEL-Steuerzeichen zum Auffüllen des Datenbereichs
0x9 - Ende der Daten
0x4 - Ende der Übertragung

Die Kommandos folgen nicht ganz exakt dem ASCII-Standard, soviel sei angemerkt.

Netzwerkprotokoll programmieren #3

Alle folgenden Artikel in dieser Artikelreihe finden Sie unter dem Tag Netzwerkprotokoll.

In diesem Artikel wird, wie versprochen, die Funktion ConvertIntToBinaryString besprochen.

void ConvertIntToBinaryString(char *resultArray, int number,
    size_t length)
{
	int count = 0;

	for (int i = length - 2; i >= 0; i--)
	{
		int bit = number >> i;

		if (bit & 1)
			*(resultArray + count) = '1';
		else
			*(resultArray + count) = '0';

		count++;
	}

	*(resultArray + length - 1) = '\0';
}

Diese Funktion erwartet die Adresse zur ersten Speicherstelle des Arrays, in das das Ergebnis geschrieben werden soll, die Zahl, die umgewandelt werden soll, und die Länge des Arrays. Die Notation wäre also im Pseudocode ConvertIntToBinaryString(dst, src, length). Da die Länge des Ergebnisarrays vom Programmierer vorgegeben ist, muss die Funktion die Größe des Arrays kennen.

In Zeile 4 wird eine Variable count auf 0 gesetzt. Diese wird in der folgenden for-Schleife hochgezählt. In der for-Schleife wird aber auch die Variable i heruntergezählt. Das hängt damit zusammen, dass der Mensch Binärzahlen von links nach rechts liest, also das MSB als erstes gelesen werden muss, und dies ist an der höchstwertigsten Stelle der Zahl.

Die Signatur der for-Schleife in Zeile 6 initialisiert die Variable i mit der Länge des Arrays – 2. Das hat damit zutun, dass die letzte Stelle in einem char-Array eine NULL ist, und von dieser Funktion nicht beachtet wird. Wiederum muss eine 1 dazu addiert werden, weil die Länge eines Arrays, also der Wert der Anzahl der Elemente eines Arrays immer einen Wert größer ist, als die adressierbaren Elemente eines Arrays (abzüglich der NULL).

Als erste Aktion innerhalb der for-Schleife in Zeile 8 wird die höchstwertigste Stelle der übergebenen Zahl per bitweisem Rechts-Shift auf die erste Stelle gesetzt und in die Variable bit gespeichert. Danach wird in Zeile 10 mit der bitweisen Operation der UND-Verknüpfung das gespeicherte Bit mit 1 verknüpft. Die 1 wird binär als 0000 0001 dargestellt.
Vereinfacht gesagt: Es wird überprüft, ob die Zahl an der ersten Stelle der Variable bit eine 1 ist. Wenn ja, dann wird eine 1 als char in das entsprechende Element im Ergebnisarray geschrieben, ansonsten eine 0.