System Engineering

FlowControl 1.3 ausgeliefert

System Engineering 23. Juni 2018

2013 wurden wir mit der Entwicklung einer Steuerung für Lackiermaschinen (FlowControl) samt Touch-Bedienung beauftragt. Die Steuerung ist im Wesentlichen für das Mischen flüssiger Komponenten (Lack, Härter, etc.) in Echtzeit verantwortlich. Seither erweitern wir kontinuierlich das System und passen es an die Wünsche der Endkunden an. Bereits 2016 begannen die Vorarbeiten für Version 1.3. Ziel war es, für einen internationalen Automobil-Zulieferer, FlowControl um eine Arbeitsauftragsverwaltung zu erweitern. Die Auftragsverwaltung ist eine separate Software, die auf Windows 10 Tablets eingesetzt wird. Sie kommuniziert mit der FlowControl Maschine, um die aktuell verfügbaren Rezepte abzufragen. Der Benutzer wählt ein Rezept, ergänzt dieses mit den benötigten Auftragsdaten und druckt den Arbeitsauftrag in Form eines Etikettes mit aufgedrucktem DataMatrix Code (Wikipedia DataMatrix-Code). Das Etikett wird anschließend auf Behälter geklebt, die mit der entsprechenden Farbmischung zu befüllen sind. Der Maschinenführer liest den DataMatrix Code mittels eines 2D Barcode-Lesegerätes ein und startet diesen per Knopfdruck. Der Kunde ist mit FlowControl 1.3 nun in der Lage, die Abarbeitung seiner Fertigungsaufträge zu optimieren und Ressourcen effizient zu nutzen. Da der Maschinenführer an der Lackiermaschine nur noch den Arbeitsauftrag einscannen und auf „Start“ drücken muss, werden zudem Fehler durch Fehleingaben vermieden.

Docker Image brainchild/ltj in Version 17.8.8 veröffentlicht

System Engineering, Public 21. Juni 2018

Wir stellen das Docker Image Linux Tomcat Java kostenfrei unter https://hub.docker.com zur Verfügung. brainchild/ltj basiert auf Ubuntu 17.04 und stellt eine Laufzeit Umgebung für Tomcat 8.0.38-2 mit Oracle Java 1.8.0_121-b13 zur Verfügung.

Für diejenigen, die selbst noch Modifikationen vornehmen möchten, sind die Quellen für das Image wie gewohnt auf GitHub zu finden. Wir freuen uns auf euer Feedback und wünschen euch viel Spaß.

Euer brainchild Team

Docker Image brainchild/packagedrone in Version 17.0140.8 veröffentlicht

System Engineering, Public 20. Juni 2018

Wir stellen das Docker Image Eclipse Package Drone kostenfrei unter https://hub.docker.com zur Verfügung. brainchild/pkgdrone basiert auf Ubuntu 17.04 und stellt eine Laufzeit Umgebung für Eclipse Package Drone 0.14.0 mit Oracle Java 1.8.0_121-b13 zur Verfügung.

Für diejenigen, die selbst noch Modifikationen vornehmen möchten, sind die Quellen für das Image wie gewohnt auf GitHub zu finden. Wir freuen uns auf euer Feedback und wünschen euch viel Spaß.

Euer brainchild Team

Letzter Feinschliff am H145FFS

System Engineering 19. Juni 2018

Wir entwickeln gemeinsam mit zwei weiteren renommierten Firmen den sogenannten H145FFS für die ADAC HEMS Academy (www.hems-academy.de) in Bonn Hangelar (näheres dazu siehe “brainchild als Simulationsentwickler für die fliegenden Gelben Engel”).

Nach rund einem Jahr neigt sich das Projekt langsam dem Ende zu. Diese Woche sind wir vor Ort in Bonn Hangelar, um den letzten Feinschliff durchzuführen, bevor die Abnahme (On-Site-Acceptance) durch den Kunden ADAC startet.

Docker Images Odoo (11.0), WJS (10.10.0) und LTJ (18.8.9) veröffentlicht

System Engineering, Public 18. Juni 2018

Seit vielen Jahren engagieren wir uns im Bereich OpenSource. Denn wir sind der Meinung, dass unser Erfolg nur mit Hilfe von Open Source möglich ist. Deshalb wollen wir der Gemeinschaft auch wieder etwas zurückgeben. Darüber berichteten wir bereits hier am 14.02.2017.

Heute freuen wir uns, sowohl Aktualisierungen als auch neue Inhalte präsentieren zu können. Zum einen haben wir Linux Tomcat Java (LTJ) auf die Version 18.8.9 aktualisiert. Zum anderen stellen wir ab sofort auch Docker Images für Odoo und den Windows Jenkins Slave (WJS) kostenfreien zur Verfügung.

Odoo ist ein OpenSource ERP-System, das die Geschäftsprozesse von kleinen und große Unternehmen abdeckt. Das brainchild Odoo Docker Image ist nicht nur zur einfacheren Handhabung während der Entwicklung von Odoo-Anwendung entwickelt worden (es unterstützt z.B. auch Remote-Debugging), sondern auch für den Produktiveinsatz, um den Rollout neuer Odoo-Versionen zu vereinfachen.

Jenkins ist ein sog. Continuous Integration Server. Dieser wird in vielen Hightech Firmen im Rahmen der Entwicklung von Software-Produkten eingesetzt, um diese vollautomatisiert zu bauen und auszuliefern. Das Docker Image Windows Jenkins Slave (WJS) stellt einen auf Microsoft Windows basierten Jenkins Build Slave zur Verfügung. Es wird dazu genutzt den Aufwand für Installation und Betrieb von Windows basierten Jenkins Build Slaves auf ein Minimum zu reduzieren.

All unsere Änderungen und Aktualisierungen findet ihr unter https://github.com/brainchild-gmbh/docker-images.

Für diejenigen, die selbst noch Modifikationen vornehmen möchten, sind die Quellen für das Image wie gewohnt auf GitHub zu finden. Wir freuen uns auf euer Feedback und wünschen euch viel Spaß.

Euer brainchild Team

Gitflow - Der Blödmann braucht Regeln

System Engineering 16. Juni 2018

Einführung

Git ([ɡɪt], engl. Blödmann) ist ein quelloffenes und frei erhältliches Versionsverwaltungssystem. Wie Subversion (SVN) einst das Concurrent Version System (CVS) ablöste, gilt SVN nun schon einige Jahre als ausrangiert. Nicht weil git hipp ist, sondern essentielle Vorteile gegenüber SVN bietet. So sind etwa Änderungen am Repository (z.B. commit) solange lokal, bis diese explizit in das entfernte Repository übertragen werden (push). Somit muss ein Entwickler nicht erst eine stabile Version seiner Software schaffen, um Änderungen in das Repository zu übernehmen, sondern kann nun jede Änderung per commit festschreiben, ohne das entfernte Repository zu beeinflussen und dadurch evtl. andere Entwickler zu stören.

Freiheit bedingt aber immer auch feste Regeln, damit ein Miteinander reibungslos funktioniert. Dieser Beitrag stellt ein Vorgehensmodell vor, das den erfolgreichen Einsatz von git in Entwicklungsprojekten, sicherstellt.

Gitflow

Gitflow basiert auf einem zentralen Repository. Es dient als Kommunikationshub für alle Entwickler. Gitflow definiert ein striktes Branching-Modell, bei dem jedem Zweig eine feste Rolle obliegt.

Master-Branch

Der Master-Branch wird ausschließlich zur Verfolgung von Releases und Zwischenversionen verwendet. Dies ermöglicht jederzeit, ausgelieferte Software-Stände einfach zu rekonstruieren.

Develop-Branch

Dieser Zweig dient zur Integration der laufenden Entwicklung. Versionen einer Software, die nicht zu einem offiziellen Release gehören, werden im Develop-Branch festgeschrieben.

Feature-Branch

Jedes Feature sollte in einem eigenen Feature-Branch entwickelt werden. Es ermöglicht, Features unabhängig von einander zu realisieren. Ein Feature zweigt von dem Stand des Develop-Branches ab, auf dem das Feature basieren soll. Ist die Entwicklung abgeschlossen, wird der Zweig in den Develop-Zweig überführt.

Release-Branch

Damit eine Auslieferung die laufende Entwicklung nicht blockiert, wird der entsprechende Entwicklungsstand auf den Release-Zweig übernommen. Dort werden nur noch abschließende Arbeiten, wie Fehlerbehebung, Dokumentation und sonstige auslieferungsorientierte Aufgaben durchgeführt. Sind diese abgeschlossen, wird der Stand in den Master-Branch übernommen. Zusätzlich wird der Develop-Branch aktualisiert, damit die Änderungen in die aktuelle Entwicklung einfließen.

Hotfix-Branch

Müssen schnell Korrekturen an Auslieferungen vorgenommen werden, sind diese auf dem HotFix-Zweig durchzuführen. Dies begünstigt, dass der restliche Workflow nicht gestört wird. Auch ist es nicht nötig erst einen Release-Zyklus abzuwarten. Nach Fertigstellung der Änderungen, werden diese ebenfalls in den Develop-Zweig übernommen.

Parametrierbare Tests mit CppUnit

System Engineering 15. Juni 2018

Es gibt einige C++ Unit Testing Frameworks. Das wohl bekannteste und meistverwendete ist CppUnit. Leider bietet es bei weitem nicht den Funktionsumfang wie sein Vorbild JUnit, was zum Teil auch mangelnder Eigenschaften der Programmiersprache C++, wie z.B. Reflection, geschuldet ist. Ein solches Feature ist z.B. die Erstellung parametrierbarer Unit Tests. Mit JUnit lässt sich solch ein Test relativ einfach implementieren. Es genügt die Unit Test Klasse mit @RunWith(Parameterized.class) zu annotieren, sowie eine Methode, annotiert mit @Parameters, welche die Testdaten als Collection<Object[]> bereitstellt, zu implementieren.

Wie eingangs erwähnt unterstützt C++ einige nützliche Eigenschaften moderner Programmiersprachen nicht, darunter auch Annotations. Dies ist zwar kein Grund parametrierbare Tests nicht zu unterstützen, dennoch hat man es bislang versäumt CppUnit mit der Unterstützung für parametrierbare Tests zu versehen. Dieser Artikel stellt eine Lösung vor, wie parametrierbare Tests für das CppUnit Framework erstellt werden können, die annähernd den selben Komfort wie jene mit JUnit bieten.

Parametrieren bedeutet in diesem Kontext, dass ein und dieselbe Test­-Methode mit den einzelnen Elementen eines Parametersatzes aufgerufen wird. Dies bietet sich dann an, wenn die Implementierung einzelner Tests, bis auf bestimmte Parameter, gleich ausfällt.

Für diesen Zweck haben wir CppUnit um diverse Makros, die zur Definition von Test-Suiten verwendet werden, erweitert. Die Makros setzen voraus, dass zwei Methoden, samt Implementierung, bereitgestellt werden

[c]
static std::vector parameters();
void testWithParameter(ParameterType& parameter);
[/c]

CPPUNIT_PARAMETERIZED_TEST_SUITE

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE ( TestFixtureType, ParameterType )
[/c]

Das Makro erweitert CPPUNIT_TEST_SUITE um den Parameter ParameterType. Dieser legt den Typ der Parameter fest.

Beispiel:

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE ( MyTest, std::string )
[/c]

CPPUNIT_PARAMETERIZED_TEST_SUITE_END

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE_END
[/c]

Dieses Makro wurde lediglich der Konsistenz halber eingeführt. Es entspricht dem Makro CPPUNIT_TEST_SUITE_END.

CPPUNIT_PARAMETERIZED_TEST_SUITE_REGSITRATION

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( TestFixtureType, ParameterType )
[/c]

Dieses Makro erweitert CPPUNIT_TEST_SUITE_REGISTRATION um den Parameter ParameterType. Es stellt die Implementierung der Testablauflogik zur Verfügung.

Beispiel:

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION ( MyTest, std::string )
[/c]

Beispiel

ParameterizedTest.hpp

[c]
class ParameterizedTest: public CPPUNIT_NS::TestFixture {
public:
ParameterizedTest();
~ParameterizedTest();
void setUp();
void tearDown();
/**
* Retrieves the test parameters. Each entry is passed as a single parameter * to a test.
*
* @return the parameters list.
*/
static std::vector parameters();
/**
* This method is called with a single test parameter.
*
* @param parameter The parameter to be used for the test.
*/
void testWithParameter(std::string parameter);
void testOrdinaryTest();
private:
CPPUNIT_PARAMETERIZED_TEST_SUITE(ParameterizedTest, std::string);
CPPUNIT_TEST(testOrdinaryTest);
CPPUNIT_PARAMETERIZED_TEST_SUITE_END();
};
[/c]

ParameterizedTest.cpp

[c]
CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION(ParameterizedTest, std::string);
ParameterizedTest::ParameterizedTest() {
// empty
}
ParameterizedTest::~ParameterizedTest() {
// empty
}
void ParameterizedTest::setUp(){
// empty
}
void ParameterizedTest::tearDown(){
// empty
}
static std::string buildParameter(size_t testNumber){ std::ostringstream result;
result &lt;&lt; “Parameter ” &lt;&lt; testNumber;
return result.str();
}
std::vector ParameterizedTest::parameters(){
std::vector result; for(size_t i = 0; i &lt; 3; i++){
result.push_back(buildParameter(i)); return result;
}
}
void ParameterizedTest::testWithParameter(std::string param){
CPPUNIT_ASSERT_EQUAL(buildParameter(m_currentTestNumber), param);
}
/**
* This test should be called after all parameterized tests.
*/
void ParameterizedTest::testOrdinaryTest(){
CPPUNIT_ASSERT_EQUAL(m_parameters.size(), m_currentTestNumber);
}
[/c]

Diese Unit Test Klasse definiert zwei Test-­Methoden: testWithParameter und testOrdinaryTest. testWithParameter wird mit einem Parameter vom Typ std::string aufgerufen. Diese werden über die Methoden parameters bereitgestellt. testOrdinaryTest soll verdeutlichen, dass neben parametrierten Tests auch “herkömmliche” Tests definiert und ausgeführt werden können. Dieser Test prüft ob der aktuelle Test­zähler der Anzahl der Parameter entspricht.

Hinweis:

Die o.g. Makros definieren interne Variablen und Methoden, u.A. auch m_currentTestNumber. Diese Variable beginnt bei 1 an zu zählen und wird nach jedem parametrierten Test inkrementiert. Sie gibt also die Nummer des aktuellen Tests an.

Es folgt der gesamte Inhalt der Datei Parameterized.hpp

[c]
#ifndef PARAMETERIZED_HPP_
#define PARAMETERIZED_HPP_
&nbsp;
#include &lt;cppunit/extensions/HelperMacros.h&gt;
&nbsp;
/**
* Extends the macro CPPUNIT_TEST_SUITE in order to easily specify a parameterized
* unit test.It expects the following methods to be specified by the implementing
* unit test:&lt;br&gt;
* &lt;ul&gt;
* &lt;li&gt;&lt;code&gt;static std::vector&lt;ParameterType&gt; parameters();&lt;/code&gt;&lt;/li&gt;
* &lt;li&gt;&lt;code&gt;void testWithParameter(ParameterType&lt;/code&gt;&lt;/li&gt;
* &lt;/ul&gt;
*
* @param ATestFixtureType Type of the test case class.
* @param ParameterType the type of a single parameter.
*
* \see CPPUNIT_TEST_SUITE
*/
#define CPPUNIT_PARAMETERIZED_TEST_SUITE( TestFixtureType, ParameterType ) \
void __test(); \
\
static std::vector&lt;ParameterType&gt; m_parameters; \
static size_t m_currentTestNumber; \
\
CPPUNIT_TEST_SUITE(TestFixtureType); \
\
m_parameters = parameters(); \
m_currentTestNumber = 0; \
\
for (size_t i = 0; i &lt; m_parameters.size(); i++) { \
std::ostringstream testName; \
testName &lt;&lt; “testWithParameter: ” &lt;&lt; i; \
\
CPPUNIT_TEST_SUITE_ADD_TEST( \
( new CPPUNIT_NS::TestCaller&lt;TestFixtureType&gt;( \
context.getTestNameFor( testName.str() ), \
&amp;TestFixtureType::__test, \
context.makeFixture() ) )); \
}
&nbsp;
/**
* Used to declare the end of a parameterized test suite.
*/
#define CPPUNIT_PARAMETERIZED_TEST_SUITE_END CPPUNIT_TEST_SUITE_END
&nbsp;
/**
* Extends the macro CPPUNIT_TEST_SUITE_REGISTRATION in order to provide
* necessary framework code in implementation file. This code actually implements
* the parameterization behavior.
*/
#define CPPUNIT_PARAMETERIZED_TEST_SUITE_REGISTRATION( TestFixtureType, ParameterType ) \
CPPUNIT_TEST_SUITE_REGISTRATION(TestFixtureType); \
\
std::vector&lt;ParameterType&gt; TestFixtureType::m_parameters; \
size_t TestFixtureType::m_currentTestNumber; \
\
void TestFixtureType::__test(){ \
\
testWithParameter(m_parameters.at(m_currentTestNumber++)); \
}
#endif /* PARAMETERIZED_HPP_ */
[/c]

Remote Method Call – Dialog der Objekte

System Engineering 13. Juni 2018

In komplexen Software-Systemen wird ziemlich schnell die Frage aufgeworfen, “Welche Technologie soll für die Kommunikation zwischen Server-Objekten und deren Repräsentanten auf dem Client eingesetzt werden?”. Dabei stehen zahlreiche Möglichkeiten zur Auswahl. Wenn es aber darum geht, eine vollständig transparente Abbildung des Objektbaumes auf dem Client zu realisieren, sodass sich dieser tatsächlich so verhält als würde er auf dem Client beheimatet sein, bleibt die Frage in der Regel unbeantwortet. Es werden dann Technologien eingesetzt, die annähernd die Anforderungen decken, der Rest wird durch entsprechende Intelligenz in der Anbindung realisiert. Allerdings sind diese Lösungen dann für den Anwendungsfall spezifisch. Seit kurzem schickt sich ein neues Framework an, genau diese Lücke zu schließen: Remote Method Call.

Hintergrund

Das EU-Forschungsprojekt OpenNode [2], beschäftigt sich im Themengebiet Smart Grid, mit der letzten Meile im Energienetz. Genauer gesagt mit neuen Konzepten zur Automatisierung der Transformatorenhäuschen, den so genannten Secondary Substations, welche die Haushalte direkt mit Strom beliefern. Sie dienen dazu, die Energie, die zur Übertragung über lange Strecken auf eine, für diesen Zweck effizientere Spannung hochtransformiert wurde, wieder auf 230 Volt herunter zu transformieren und den Verbrauchern ausfallsicher zur Verfügung zu stellen. Dazu dienen im Wesentlichen mehrere Transformatoren und Elektronik zur Überwachung und Lastverteilung. Künftig sollen diese Stationen an die Energieversorger angeschlossen werden, sodass diese direkt mit ihnen Daten austauschen und kontrollierend einwirken können. Weiterhin werden die Secondary Substations als Datensammler für Energiedaten der Haushalte ausgebaut, sodass wir langfristig unsere Stromrechnung monatlich, nach dem tatsächlichen Verbrauch bezahlen werden. Dazu kommuniziert das Trafohäuschen mit jedem einzelnen, intelligenten Stromzähler (Smart Meter) der Haushalte, basierend auf Powerline Communication, also Datenaustausch über das Energienetz selbst.

Die Mission

Im Rahmen des Projektes wird ein Prototyp eines Embedded Servers realisiert, der Secondary Substation Node (SSN). Dieser dient als Bindeglied zwischen den  Supervisory Control and Data Acquisition (SCADA) [3] Systemen der Energieversorger,  den Geräten und Anlagen innerhalb des Trafohäuschens, sowie den einzelnen Stromzählern der Haushalte. Dazu finden vielfältige Kommunikationstechnologien ihre Anwendung, wie IEC60870-104 [4] zur Anbindung der SCADA Systeme und der Automatisierungstechnik, DLMS/COSEM [5] als Datenträger der Energiedaten der Smart Meter, PRIME [6] als Powerline basiertes Kommunikationsprotokoll, uvam.

Man hat sich sehr früh im Projekt entschieden, weitestgehend offene Technologien, sowohl für das Betriebssystem (Ubuntu Server), als auch zur Entwicklung der Software (Java, OSGi, Web-Services), zu verwenden.

Der Server hält permanent eine Repräsentation der Prozessdaten als IEC61850 [7] basiertes Datenmodell für den Zugriff durch Applikationen auf dem SSN, die Mess-, Steuer- und Regelungsaufgaben wahrnehmen und für die entfernten SCADA Systeme, welche die Prozesshoheit besitzen, bereit.

Zur einfachen Visualisierung der aktuellen Prozessdaten und für Diagnosezwecke während der Entwicklung, sollte ein “kleines” Werkzeug realisiert werden, das den direkten Einblick in das IEC61850 basierte Java Objektmodell auf dem SSN, mit der Möglichkeit, Messwerte grafisch darzustellen, erlaubt. Die Software auf dem SSN besteht dabei aus einer Vielzahl OSGi-Bundels, die in einer Equinox [8] Laufzeitumgebung betrieben werden.

Ziel war es möglichst die gleiche Repräsentation des Objektmodells innerhalb des Werkzeuges zu verwenden, wie sie auf dem Server zu finden ist. Dadurch wird nicht zu letzt die Austauschbarkeit der Kommunikationsanbindung an das Datenmodell gewährleistet. Mal wird das Modell auf dem Client betrieben, mal soll via Netzwerk auf das Modell des SSN zugegriffen werden.

Abb. 1 – Benachrichtigung in der Objekthirarchie

Das Objektmodell ist in Abbildung 1 skizziert. Es besteht aus einzelnen Knoten, die in einer Eltern-Kind-Beziehung stehen. Andere Knoten erlaubt es, Listener zu registrieren, die über Veränderungen an dem Objekt oder einem seiner Kinder informieren. Das bedeutet: Wird ein Knoten modifiziert, meldet er dies seinem Eltern-Knoten und allen Listenern, die auf diesem Knoten registriert sind. Der Eltern-Knoten meldet das Ereignis wiederum seinem Eltern-Knoten und allen Listenern, die auf ihm registriert sind. Der Vorgang wird so lange fortgeführt, bis das Ereignis am obersten Eltern-Knoten angekommen ist. Damit haben Anwendungen die Möglichkeit, auf dedizierten Knoten – oder irgendwo in der Hierarchie darüber – zu horchen, um so über Änderungen an untergeordneten Knoten informiert zu werden.

Das Problem

Es galt nun, dieses Modell auf dem SSN via Netzwerk auf einem Client in dem Visualisierungswerkzeug zu nutzen und das möglichst so, dass es sich genauso wie auf dem Server verhält. Selbstverständlich denkt man bei einem objektbasierten Zugriff auf entfernte Objekte an Remote Method Invocation (RMI) [9].

Es hat sich aber schnell herausgestellt, dass RMI den Herausforderungen bei weitem nicht gewachsen war. Es ist zwar prinzipiell einfach zu verwenden, man kann relativ schnell entfernte Objekte anbinden, hat aber eine Reihe von Nachteilen, die es unmöglich machen, ein Objektmodell, wie es beschrieben wurde, wirklich völlig transparent anzubinden.

Es genügt nicht, einen Methodenaufruf an das entfernte Objekt zu delegieren und das Ergebnis wieder zum Client zurück zu senden. Ferner müssen Events, die durch Änderung bei einem der Teilnehmer hervorgerufen wurden ebenfalls bei allen anderen Teilnehmern propagiert werden. D.h. jeder der Teilnehmer benötigt potentiell Zugriff auf den anderen.

Konkreter gesagt: Registriert z.B. Der Client einen Listener auf einem Objekt, muss dieser letztendlich auf dem Server-Objekt registriert werden. Wird ein Event gefeuert, wird eben dieser Listener auf dem Server-Objekt benachrichtig. D.h. Diese Methode wird aufgerufen, was zur Folge hat, dass dieser Aufruf zurück zum Client delegiert wird, um den tatsächlichen Listener zu benachrichtigen. Das erfordert also eine bidirektionale Kommunikation zwischen Server-Objekten und deren Repräsentanten auf dem Client.

RMI kann das nicht leisten. Es wird EIN Objekt von der Server-Anwendung bereitgestellt, das mittels eines eindeutigen Namens adressiert wird. Ruft der Client eine Methode auf dem Objekt auf, wird dieser Aufruf via Netzwerk an das Server-Objekt geschickt, ausgeführt und das Ergebnis an den Aufrufer zurückgeleitet. Die eigentlichen Nutzdaten, also Übergabeparameter der Methode und der Return Value, bzw. die Exception, im Falle eines Fehlers, wird mittels Serialisierung [10] in einen Binärstrom codiert und vom Empfänger wieder decodiert. Damit werden diese Objekte kopiert. Es besteht keine Verbindung mehr zu dem ursprünglichen Objekt.

Ein zweiter wesentlicher Aspekt bei RMI ist, dass Methodenaufrufe ausschließlich an Server-Objekte gerichtet werden können. Damit der Server eine Method, z.B. bei den genannten Listenern, aufrufen kann, müssen die Listener via RMI zur Verfügung gestellt werden. der Server muss diese dann explizit Adressieren um eine Verbindung zu den Listenern herstellen zu können. Es muss zusätzliche Intelligenz in Client und Server implementiert werden, damit man annähernd das genannte Verhalten realisieren kann.

Der dritte essentielle Haken an RMI ist, dass mit einer Verbindung immer nur ein einziges Objekt für entfernte Zugriffe zur Verfügung steht. Will man also auf jedem Objekt in einen Objektbaum mit mehreren tausend Objekten entfernte Methoden aufrufen, benötigt man ebenso viele Verbindung zu den Server-Objekten und bei RMI nochmal genauso viele Namen, um diese Objekte überhaupt ansprechen zu können. Bei einer bidirektionalen Kommunikation müssen dann nochmal Verbindungen mit der gleichen Anzahl in der anderen Richtung verwaltet werden. Bei einem Objektmodell mit über 3000 Knoten, wie es aktuell bei dem SSN vorliegt, würden über 6000 Verbindungen auf Client und Server benötigt.

Die Lösung

Wir bei brainchild haben nicht lang Überlegt und ein OpenSource-Projekt daraus gemacht, das wir mit eigenen Mitteln realisieren, um diese Lücke zu schließen und eine frei zugängliche Lösung anzubieten: Remote Method Call [11].

Der Name Remote Method Call geht auf eine Entwicklung von Adam Bien von vor über einem Jahrzehnt zurück. Er hatte damals im Java Magazin über die damaligen Probleme von RMI – man musste damals z.B. noch den RMI Compiler bemühen, um den Kommunikations-Code zu generieren – berichtet und eine passende Lösung präsentiert. Allerdings war das damalige RMC funktional identisch zum Sun RMI.

Was ist nun an unserem RMC anders?

  1. Man kann mit nur zwei Zeilen Code auf Client und Server eine Verbindung zwischen zwei Objekten herstellen.
  2. Es erweitert die Konventionen von RMI
    • Objekte bzw. Interfaces, die für Remote-Zugriffe zur Verfügung stehen sollen, müssen entweder java.rmi.Remote implementieren oder mit der Annotation de.brainchild.rmc.Remote versehen sein.
    • Alle Objekte, die übertragen werden, also Parameter und Rückgabewerte, müssen serialisierbar sein, siehe [10].
    • Methoden können explizit vom Remote-Zugriff ausgenommen werden, entweder indem sie mit der Annotation de.brainchild.Local markiert werden, oder in dem man diese Methoden in einer Komma getrennten Liste der System-Property de.brainchild.rmc.executeLocal zuweist.
    • Im Gegensatz zu RMI ist RMC nicht ausschließlich auf die Kommunikation via Sockets beschränkt. Es ist vielmehr völlig unabhängig vom Übertragungsmedium. Es ist sehr einfach andere Kommunikationsmedien, wie z.B. Busse oder serielle Medien anzubinden. Dazu muss nur eine Klasse vom Typ de.brainchild.rmc.ConnectionFactory bereit gestellt werden.
    • In einem RMC Kommunikationsszenarium gibt es einen Server und beliebig viele Clients. Der Server exportiert das zentrale Objekt, z.B. Eine Factory, um an das Datenmodell zu gelangen, bzw. das Datenmodell selbst – was genau, ist anwendungsspezifisch. Der Client bindet das entsprechende Interface an das Server-Objekt.

Und wie funktioniert RMC?

Abb. 2 – Kommunikation zwischen lokalem- und Remote- Objektbaum via RMC

Abbildung 2 skizziert den Aufbau von RMC. Jedes entfernte Objekt wird vom RMC-Framework mit einer Hülle versehen, dem de.brainchild.rmc.RemoteObject. Diese Hülle ist verantwortlich für das Delegieren entfernter Aufrufe und bedient sich dabei einer de.brainchild.Connection, die mittels der de.brainchild.rmc.ConnectionFactory erzeugt wird. Außerdem nimmt diese Hülle auch die Methodenaufrufe entgegen, führt sie auf dem entsprechenden Zielobjekt aus und sendet das Ergebnis, Rückgabewerte oder Exception, zurück an den Aufrufer. Werden nun Parameter oder Rückgabewerte übertragen, die ebenfalls als java.rmi.Remote, bzw. de.brainchild.rmc.Remote, gekennzeichnet sind, registriert das empfangende RemoteObject sie als solche und dekoriert sie ebenfalls als RemoteObject. Damit stehen sie wiederum für entfernte Aufrufe zur Verfügung. Dem eigentlichen, empfangenden Zielobjekt, z.B. beim Registrieren eines Listeners, wird das stellvertretende RemoteObject übergeben. Ruft dieses nun eine Methode auf dem Objekt auf, wie z.B. die des Listeners, wird der Aufruf an das Zielobjekt übertragen, womit das “Spiel” von vorn beginnt. Die ganze Magie im Hintergrund der Hülle wird mittels Dynamic Proxies [12] realisiert.

Referenzen

[1] https://www.brain-child.de

[2] https://www.opennode.eu

[3] https://de.wikipedia.org/wiki/Supervisory_Control_and_Data_Acquisition

[4] https://de.wikipedia.org/wiki/IEC_60870

[5] https://www.dlms.com

[6] https://www.iberdrola.es/webibd/corporativa/iberdrola?IDPAG=ENWEBPROVEEBASDOCCONT

[7] https://de.wikipedia.org/wiki/IEC_61850

[8] https://www.eclipse.org/equinox

[9] https://www.oracle.com/technetwork/java/javase/tech/index-jsp-136424.html

[10] https://download.oracle.com/javase/7/docs/technotes/guides/serialization/index.html

[11] https://sourceforge.net/p/jrmc/home/Home/

[12] https://download.oracle.com/javase/7/docs/technotes/guides/reflection/proxy.html

Scroll Up