Inhalt
Topic:Programming.ModulStructure.Dependencies
Die Beachtung von Abhängigkeiten in der Software sind ein wichtiges Thema der Softwarepfege, oft unterschätzt. Intuitiv erstellte Software ohne Abhängigkeitsprüfung und Diskussion läuft zunächst nach einer gewissen Inbetriebnahmephase, doch bei Änderungen an der einen Stelle gibt es oft unerwartete Nebeneffekte... Software, bei der Abhängigkeiten richtig designed sind, kann man an einer Stelle korrigieren, und hat entweder keine Nebenwirkungen, oder Korrekturen an allen ähnlichen betreffenden Stellen gratis mit, bevor dort Fehler überhaupt auffalllen.
siehe auch DependenciesInSoftware
Topic:LibJc.Layer.OsLayerAndAppl
Typisch für Embedded-Applikationen ist, dass diese eng mit einer bestimmten Hardware zusammenarbeiten müssen. Die Hardware besteht aus möglicherweise Spezialprozessoren, oft einem oder mehreren FPGA (Field Programmable Gate Array, programmierbare Logik) und spezifischen Signalverarbeitungen, an die möglicherweise Leistungshalbleiter, Schütze und Relais, spezielle Messwerterfassungen angekoppelt sind. Die Verarbeitung im Prozessor erfolgt im Takt der Einganssignale (Echtzeit) und ist oft an ein spezielles mehr oder weniger allgemein gebräuchliches Betriebssystem gekoppelt.
Die Software ist mehr und mehr umfangreich, da das Leistungsvermögen der Prozessoren steigt und die Eigenschaften der Geräte diesem Leistungsvermögen entsprechenden Wünsche erfüllen sollen. Wo früher eine spezielle Assemblerprogrammierung ausgereicht hat, um den geforderten Funktionsumfang zu realisieren, kann heute eine komplex Software implementiert werden.
Bild: Layer und Applikationssoftware
Das Bild zeigt die Verbindung der Layer ab der Hardware bis zu CRuntimeJavalike mit der Applikation.
Die Applikation ist teilbar in zwei Bereiche:
orange: Software, die eng an die Hardware und das Gerät als solches gebunden ist,
gelb. Software, die zwar die Eigenschaften des Gerätes maßgeblich bestimmt, aber nicht unbedingt auf diese Hardware ausgerichtet sein muss.
Früher hat man sich vorderhand auf den orangenen Teil konzentriert. Die Software was sowieso hardwaregebunden, wurde in Assembler speziell auf die Hardware zugeschnitten. Heute sind die Anforderungen an die Funktionalität gestiegen und die Entwicklungszeiten und -kapazitäten der Software sind dennoch nicht größer geworden. Diese Schere kann man nur überwinden, indem der Softwareentwicklungs- und -testprozess effektiviert wird und Software wiederverwendet wird. Damit kann sich diese Software aber nicht auf die bestimmte Hardware konzentrieren sondern muss allgemeiner realisiert werden.
Der Teil der Software, der an die Hardware gebunden sein muss, wird neben speziellen Bedingungen der Hardwareankopplung auch die spezifischen Leistungseigenschaften des vorhandenen Betriebssystems (OS) nutzen wollen und müssen. Dieser Teil der Software muss spezifisch programmiert werden.
Der größere Teil der Software, der übergeordnete Teil, soll aber keine Hardwarespezifika beachten müssen (diese werden durch den orangenen Teil gekapselt) und braucht oft auch nicht die spezifischen Eigenschaften des Betriebssystems ausnutzen. Dieser Teil der Software ist mit den nachfolgenden Darstellungen über ein OSAL- und Jc-Layer angesprochen. Dafür gilt:
Wiederverwendung von Softwareteilen und Ermöglichen der Wiederverwendung spart Entwicklungskosten. Das ist ein sekundärer Aspekt, denn wer denkt heute schon an die Wiederverwendbarkeit in Hardwareplattform B für Gerätegeneration X, wenn es konkret um die Entwicklung der Gerätegeneration A geht (?)
Testbarkeit der Funktionalität off-line, am Büroarbeitstisch am PC. Das ist zweistufig:
Einerseits kann mindestens der Programmquelltext bequem in einer IDE erstellt werden, die für PC-Programme gängig ist und oft gut beherrscht wird. Man kann einzelne Algorithmen manuell antesten.
Andererseits kann aber auch ein kompletter Modultest am PC erfolgen, in dem die definierte Funktionaltiät der Software-Module in einer Testumgebung mit Test-Stimuli geprüft wird. Wenn ein solcher Test erfolgt, sind meist die Überraschungen über nicht erwartete Features des Gerätes geringer und die Fehler können schneller lokalisiert werden.
Das ein solcher Test auf PC-/Modul-Ebene ein Integrationstest des Gerätes nicht ersetzt, dürfte klar sein. Beide Test-Strategien sind notwendig.
Die Erstellungszeit der Software vermindert sich oft beträchtlich, wenn die Software am PC mit einer gängigen IDE erstellt und vorgetestet wird. Faktoren liegen im Bereich 2 bis 5.
Topic:LibJc.Layer.os_types_def
Das unterste hier beschriebene Layer ist das Headerfile <os_types_def.h>
. Dort sind Basistypen so definiert und in ihrer Implementierung so festgelegt, dass der Compiler sie im Zusammenspiel mit
dem Zielprozessor in der notwendigen Art realisiert. Dazu gehören
die Grundtypen int8
, int16
, int32
, int64
und ihre uint..
- Entsprechungen,
für C die Definition von bool
, true
und false
(in C++ Spracheigenschaften)
Typ uint
(sonst als unsigned int
zu schreiben)
null
als Null-Zeiger
die Definition MemUnit
als Typ einer Speicherzelle (ein Adress-Schritt): nicht alle Prozessoren sind byte-orientiert in ihrer Adressierung des Speichers,
Eine Struktur OS_ValuePtr
, die einen Int-Wert und einen Zeiger so vereint, dass eine Rückgabe als return
-Wert möglichst effektiv in Registern erfolgt, die Definition muss sich also nach dem Prozessor-Design und den Compilereigenschaften
richten,
Makros METHOD_C
GNU_PACKED
, die für den Anwender damit verwendbar sind unabhängig vom verwendeten Compiler und Plattformeigenschaften,
Einige Defines wie __OS_IS_XYZ__
und OSAL_LITTLEENDIAN
oder OSAL_BIGENDIAN
, die in übergeordneten Quellen verwendet werden können zur bedingten Compilierung und das Zielsystem beschreiben. Es wird
allerdings empfohlen, die bedingte Compilierung nur übersichtlich, allgemeingültig und sparsam zu verwenden.
Dieses Headerfile soll überall als Erstes eingezogen werden. Das ist meist schon wegen der Basistypen notwendig, die in C/C++ nicht in der notwendigen Weise standardisiert sind.
Implementierungen des Betriebssystems, die oft von Fremdherstellern stammen, oder Software aus anderen Bereichen verwenden
meist eine ähnliche Strategien und Headerfiles für diesen Aspekt. So heißen beispielsweise die Grundtypen auch i32
oder dergleichen, oder FS_I32
mit einem Präfix wegen der Eindeutigkeit. Diese Tatsache ist im Bild mit einem weiteren rotem Kästchen unterhalb OS
angedeutet. Es ist so, dass eine Abstimmung der Verwendung von Bezeichnungen zu einem Fremdhersteller kaum erfolgen kann,
hier hat ein C-Standardisierungsgremium leider geschlafen. Eine Abstimmung von vorhandener Software kann wegen des Änderungsaufwandes
nicht erfolgen, und eine Abstimmung mit anderen Softwareentwicklungsabteilungen gestaltet sich oft schwierig, weil jeder sich
seine eigene Welt bereits eingerichtet hat. Allerdings ist das kein Problem, da zueinander passende Typdefinitionen vom Compiler
erkannt werden. So ist eine Zuweisung eines FS_I32
-returnwertes zu einem int32
überhaupt kein Problem. Diese Welten können parallel bestehen bleiben. Man sollte in der eigenen Software die eigenen Typbezeichnungen
wählen.
Topic:LibJc.Layer.base
Es gibt eine Reihe von Basisfunktionalitäten,
die nicht von einem Betriebssystem abhängen sondern rein algorithmischer Natur sind,
die gegebenenfalls im Standard-C-Sprachumfang definiert sind und auch in einer Standard-C-Library zu finden ist, jedoch soll die Implementierung möglicherweise für embedded Anwendungen angepasst erstellt werden; möglicherweise ist der Funktionsumfang im C-Standard auch unscharf bestimmt,
die nicht im Standard-C definiert sind aber in den meisten Anwendungen typischerweise vorhanden sind und sonst gegebenfalls zwar funktionell ähnlich aber doch irgendwie verschieden definiert werden,
die einfache und unabhängige Funktionalitäten aufweisen, keine weiteren Module benötigen.
Solche Funktionalitäten können in einer Basis-Library zusammengefasst werden. Diese Basislibrary kann für embedded-Anwendungen
eine libc
auch ersetzen. Diese Basis-Library soll auf der untersten Ebene zur Verfügung stehen und demzufolge in den OSAL-Betriebssystemanpassungen
verwendet werden können.
Topic:LibJc.Layer.osAdaption
Software sollte aus Anwendersicht unabhängig vom Betriebssystem sein. Nur dann ist es einerseits möglich, Algorithmen und Funktionalitäten in einer anderen Umgebung, gegebenenfalls mit simulierten Außenschnittstellen, zu testen. Andererseits wird so ein Einsatz auf verschiedenen Hardwareplattformen möglich. Das trifft insbesondere für embedded-Software zu.
Um diese Unabhängigkeit vom Betriebssystem zu erreichen, ist folgend eine OSAL-Schicht definiert (OSAL = Operation System Adaption Layer). Die Implementierung ist dabei os-spezifisch. Die Definition der Schnittstellen einschließlich des Programmtextes der Headerfiles ist aber os-unspezifisch. Die meisten Betriebssysteme bieten vergleichbare Funktionen, nur alle ein wenig anders. Die OSAL-Implementierung ist zumeist eine relativ dünne Schale um die vom Betriebssystem bereitgestellten Funktionen. In wenigen Fällen, für unpassende Betriebssysteme, muss mehr getan werden.
Topic:LibJc.Layer.framework
C und C++ definiert zuwenig Dinge als einheitlichen Standard. Das ist ein Zugeständnis an Maschninen-Nähe - dies braucht Flexibilität.
So ist beispielsweise die Datenbreite von int
-Variable für jeden Compiler anders wählbar und zweckmäßigerweise an die Registerbreite des Prozessors gebunden. long
ist größer oder gleich int
, es ist aber nirgends festgeschrieben, dass long
immer 32 bit breit sein muss. Ähnlich ist es mit short
. Was in C und auch in C++ fehlt, ist die Definition von Basistypen mit einer plattformunabhängig festgelegten Bitbreite,
die für eine portable Programmierung wichtig wäre. Die Folge ist, dass in jedem Projekt und/oder für jede Plattform anders
bestimmte Festlegungen getroffen werden, wie Datentypen für feste Bitbreiten definiert werden. Beispiele sind i16
, int16
, Int16
, I2
für den Integer-Datentyp, der 16 bit umfasst. Ziel von Basis-Headerfiles eines Framework ist es, solche Definitionen einheitlich
festzulegen und allen Anwenderprogrammen zugänglich zu machen. Dabei sind vorhandene oder zugelieferte Software, die gegebenenfalls
andere Regeln hat, mit zu integrieren. So ist es kein Widerspruch, verschiedene Bezeichnungen zuzulassen und per #define
gleichzusetzen, wohl aber auf die Verwendung einer bestimmten Bezeichnung für Neuentwicklungen hinzuweisen.
Die Definition von Basis-Funktionalitäten umfasst mehr noch als diese Grundtypen. Genaugenommen wird damit eine C/C++-Spracherweiterung definiert, die projektübergreifend gelten sollte. Je allgemeingültiger und anwendungsbreiter diese ist, desto weniger Detail- und Nachaufwand hat man bei der Pflege und Integration von Software. Ähnliche Spracherweiterungen bringen die meisten zugekauften Programmierumgebungen oder Standard-Library-Angebote ebenfalls mit. Das Problem hierbei ist, dass es keine Einheitlichkeit gibt und dass das Anpassungen einen gegebenenfalls schwer handhabbaren Eingriff in ein Kaufteil darstellen. Man hat Schwierigkeiten, wenn man plattformübergreifend programmieren will, sich aber an eine bestimmte zugekaufte Plattform orientiert. Daher ist es oft zweckmäßiger, sich nicht eine bestimmte Plattform unterzuordnen sondern stattdessen das allgemeingültige Framework selbst zu definieren und an die einzusetzenden Plattformen anzupassen. Der Aufwand dazu ist oft niedriger als die sonst notwendigen Anpassungsprobleme.
Topic:LibJc.Layer.framework.platformConv
Bestimmte Verhaltensweisen sind für eine Plattform anders zu definieren als für eine andere, obwohl die Aufrufbedingungen gleich sind. Typisches Beispiel ist die Reaktion auf Fehler. Während in einer Testumgebung bei Fehlern einfach angehalten werden soll, um den Gesamtzustand untersuchen zu können (Debugger-Nutzung), kann auf einer Zielplattform beispielsweise ein Logeintrag erzeugt werden, gegebenenfalls in einem internen Speicher, aber das Gerät soll möglichst weiterarbeiten.
Diese Festlegungen gelten unabhängig von der Applikation für das Gerät oder die Art der Anwendung oder für die Platform. Daher werden sie auf dem Layer des Frameworks plattformabhängig implementiert und stehen dabei allen Layern oberhalb zur Verfügung.
Topic:LibJc.Layer.framework.platformConvFromOSAL
Um auch Betriebssystem-Meldungen über diese Funktionalität führen zu können, ist im OSAL-Layer ein callback-Mechanismus eingebaut, der dynamisch auf dieses Layer verweist. Damit kann die OSAL-Implementierung zwar unabhängig von der Art der Anwendung (der gewünschten Fehlerreaktion) sein, dennoch ist an dieser Stelle bestimmbar, wie eine Fehlerbehandlung erfolgt. Man muss also nicht das OSAL-Layer einer Art der Anwendung anpassen.
Topic:LibJc.Layer.Jc
Das hier genannte Jc-Layer ist das eigentliche CRuntimeJavalike-Layer. Es enthält diejenigen Funktionalitäten, die zur Abbildung der Java-Basisklassen notwendig sind und in C anders zu implementieren sind als in Java. Teils ist der Java-Code auch im C nachempfunden worden.
Die Problematik des Fehlens eines wirklichen Standards in C und C++ für Basisfunktionen führt zu der Frage, ob eine Anlehnung an eine der vorhandenen Quasi-Standards erfolgen soll. Eine Anlehnung an ein vorhandens System und eine entsprechende Umsetzung für die konkreten Belange ist immer eine bessere Wahl als Algorithmen und Schnittstellen frei selbst erfinden, da die Erfahrungen des vorhandenen Systems einfließen.
Die Programmiersprache Java enthält in ihrem Standardausbau alle notwendigen Basisfunktionalitäten für
Multithreading (Threads verwaltet, Synchronisationsmechanismen)
Container (Listen, Queues, Maps)
String-Verarbeitung
Umgang mit Datum und Zeit
Middle-Level-IO-Organisation wie BufferedReader
Die Entwicklung von Java konte hierbei profitieren von allen Erfahrungen der Programmierung, die bis zum Erscheinen von Java etwa 1995 vorhanden waren. Einhergehend damit ist auch die Reduktion auf einfache Mechanismen dort, wo eine Vorentwicklung zunächst übermäßig komplexes hervorgebracht hat und aus Kompatibilitätsgründen weiter beibehalten muss. Neuere Entwicklungen wie beispielsweise die Lock-Free-Programmierung oder Umgang mit Zeichencodierungen sind in Java als Erweiterungen eingeflossen. Damit bietet Java ein Basissystem, welches fein abgestimmt insbesondere für embedded control geeignet ist. Die ursprüngliche Fokusierung von Java lag auf Internet-Anwendungen, die auf jedem System eingebettet laufen sollten. Aufgrund des Potenzials von Java wurde dieses aber dann neben Standard-PC-Anwendungen in embedded Systemen angewendet und ist dort bewährt. Damit ist eine Anlehnung an Funktionalitäten, wie sie in Java vorhanden sind, für ein allgemeines Basissystem geeignet.
Java arbeitet immer mit Unterstützung einer sogenannten Virtuellen Maschine (VM) und ist somit abkoppelbar von Eigenheiten des umgebenden Systems. Die VM basiert allerdings auch auf einfachen Abarbeitungsregeln, die meisten Sprachkonstrukte und Funktionalitäten in Java passen auf eine maschinenorientierte Abarbeitung in C. Die VM-Orientierung stellt also kein Hindernis der Nutzung von Java-Prinzipien dar.
Sämtliche Algorithmen des Submoduls Jc sind in C programmiert und getestet und benötigen weder eine VM noch etwas anderes
aus Java. Die Java-Orientierung besteht lediglich in der Ähnlichkeit der Wahl der Funktionalitäten einschießlich der Bezeichnungen.
So gibt es beispielsweise eine Klasse LinkedListJc
, die funktionell der Klasse java.util.LinkedList
entspricht. Eine C-Routine add_LinkedListJc(...)
führt funktionell vergleichbares aus wie die add(...)
-Methode der Java-Klasse. Es sind nur jeweils diejenigen Funktionalitäten aus Java realisiert, für die es auch konkrete Anwendungsfälle
gibt.
Alle Quellen des Layers Jc sind zuerst in C programmiert und auch mit einem C-Compiler übersetzbar. Einer Klasse in Java entspricht immer ein Komplex von C-Routinen mit einer entsprechend benannten Datenstruktur. Für die Erleichterung der Anwendung in C++ gibt es jeweils einen C++-Klasse als Wrapper. Insbesondere bei String-Verkettungen können hierbei Schreib-Erleichterungen genutzt werden, da C++ die Definition von Operatoren gestattet. Ein Zugriff aus einem UML-Modell wie beispielsweise mit (Rhapsody ist ebenfalls möglich mit Bereitstellung der entsprechenden Modell-Elemente und Verbindung mit der hier vorhandenen externen Implementierung..
Topic:LibJc.Layer.WhatToUse
Diejenige Software, die speziell für die Zielplattform geschrieben ist und nur mit der Zielplattform gemeinsam sinnvoll testbar ist, darf alle Schnittstellen, die auf der Zielplattform vorhanden sind, nutzen. Das sind direkte Zugriffe auf die Hardware, spezielle Eigenschaften des Betriebssystems, aber auch die hier vorgestellten Layer unterhalb der Applikation. Bei dieser Software handelt es sich um die Treiber- und Interruptebene. Diese Softwareteile sind meist algorithmisch nicht komplex. Die Applikationssoftware ruft diese Teile auf bzw. wird dort aufgerufen.
Reine Applikationssoftware, die auf einem PC modulgetestet werden soll, darf direkt weder Hardwarezugriffe ausführen noch die Schnittstellen des Betriebssystems direkt nutzen. Es sind nur Zugriffe über die OSAL-Schicht zulässig. Diese Applikationssoftware kann aber die plattformorientierten Routinen aufrufen, die in der Testumgebung am PC entsprechend zu emulieren sind.
Es kann empfohlen werden, aus der Sicht der Applikationssoftware auch nicht die OSAL-Routinen aufzurufen. Die OSAL-Schicht ist damit 'nur' die Anpassungsschicht zum Betriebssystem aus den darüber liegenden Systemlayern Fwc, Jc und den plattformorientierten Routinen. Das ist damit zu begründen, dass die darüberliegenden Schichten Fwc und Jc noch einige Checks ausführen können. Außerdem sind dann bei Änderung der OSAL-Schnittstelle keine Anwender-Routinen betroffen. Fwc und Jc kapseln die OSAL-Schicht also in zweckmäßiger Art nochmals.
Die Frage, ob eine Anwenderroutine sich stark an der Jc-Schicht orientiert, hängt davon ab, ob das Javalike-Konzept von allen
Softwareerstellern der Anwenderschicht akzeptiert wird. Einerseits gibt es bei bereichsübergreifender Zusammenarbeit oft Abstimmungsschwierigkeiten,
eine Javalike-Herangehensweise zu vereinbaren ist dabei nochmals schwieriger als sich auf bestimmte OS-Zugänge über OSAL zu
einigen. Daher ist die kleinere Schnittmenge schonmal ein Erfolg. Möglicherweise kann dann der zweite Schritt nach Bewährung
des Ersten gegangen werden. Andererseits ist es auch schon vorgekommen, dass Java in bestimmten Entwicklerkreisen vollständig
abgelehnt wurde, sei es aus Unkenntnis oder Vorbehalten. Letzteres ermöglicht keine Sachdiskussion, so dass diese Ebene einer
gemeinsamen Softwareplattform sich mindestens für ausgetauschte Software zurückziehen muss. Das ist auch der Grund der Definition
eines extra Fwc
-Layers, das bei genauerem Hinsehen Java-like-Konzepte enthält, die aber nicht so benannt wurden. Diese Teile müssen in gemeinsamer
Software enthalten sein, wenn darauf aufbauender Teile die Javalike-Konzepte nutzen wollen.
simple time conversion, string-base, Ausgabeformatierungen (sprintf-like),
TODO (Basis-Datentypen, Threads, File-IO, Mutex, Synchronisation, Mem-Zugriff, Socket)
TODO (Exception, LogMessage, Object, Framework-Conventions
Topic:LibJc.Layer.Jc.fnOverview
Folgende Funktionalitäten sind im Jc-Modul realisiert (Überblick)
Allgemeines Superclass Object
. In diesem Zusammenhang sind Arrays mit einschließender Längenangabe für eine sichere Programmierung möglich.
Multithreading: synchronized
, Object.wait()
und Object.notify()
, damit sind jegliche Belange der Prozess-Synchronisation abdeckbar.
Reflection: Das Reflectionprinzip wird benutzt um auf Daten symbolisch zugreifen zu können, für Testzwecke (Inspector), für Trace-Zwecke (dort als FlexItem bezeichnet). Es ist möglich, das Reflectionprinzip für eine interpretative Abarbeitung von Statecharts zu nutzen. Dabei können Abläufe in UML (Rhapsody) projektiert werden, es muss aber nicht jeweils eine neu zu testende Executable erstellt werden sondern es wird ein Script erzeugt, das im Automatisierungsgerät aktualisiert wird.
String-Verarbeitung: String
und StringBuffer
. Eine ursprünglich in Java realisierte Klasse StringPart oberhalb des Java-Standards steht ebenfalls zur Verfügung. * Container:
Realisiert sind die wesentlichen Funktionalitäten einer java.util.LinkedList
und eine java.util.concurrent.ConcurrentLinkedQueue
(Lock free programming). Ein java.util.TreeMap
ist nicht realisiert aber angedacht (binär organisierter Baum von Objekten).
Date: Format-Konvertierungen von Zeitangaben. Diese basieren auf den Routinen in fw_Formatter.*
.
System.arraycopy()
.
File-IO-Klassen java.io.File
, java.io.FileWriter
, java.io.FileReader
, java.io.BufferedReader
, (derzeit jeweils nur soweit wie benötigt).
Das Submodul Jc basiert auf dem Submodul Fwc und auf der OSAL-Schicht.