Inhalt
Topic:.inspc.targetProxyTarget.
Der Aufwand, den Reflectionmechanismus in ein Zielsystem (target) zu implementieren ist durchaus hoch. Es muss relativ viel const-Speicher vorhanden sein, um die entsprechenden symbolischen Zugriffe zu definieren, es muss eine Stringverarbeitung unterstützt werden und relativ viele C-Quellen aus der CRuntimeJavalike müssen mit compiliert werden. Für den Zugriff muss es einen leistungsfähigen Mechanismus, Sockets und Ethernet, geben.
Das alles kann nicht immer vorausgesetzt werden:
Bei wenig leistungsfähigen Prozessoren
Wenn keine Stringverarbeitung angeboten wird bzw. diese nicht so gut unterstützt wird, insbesondere bei Signalprozessoren.
Wenn im Zielsystem möglichst wenig zusätzlicher Code eingebunden werden soll.
Wenn die Socket- und Ethernetanbindung nicht verfügbar ist bzw. bei Spezialsystemen eingeschränkt ist auf bestimmte Protokolle. Keine allgemeine API.
In diesen Fällen ist es möglich, einen Vermittler (Proxy) zu verwenden. Das Proxy kennt die symbolischen Reflection-Informationen des Target und wird jeweils mit aktualisiert, wenn sich die Software im Target ändert. Im Proxy wird eine Vorverarbeitung durchgeführt. Das Proxy kommuniziert mit dem Target über ein relativ einfaches Protokoll, was sich beispielsweise mit Dual-Port-RAM realisieren lässt, wenn beide Prozessoren auf der selben Hardware laufen. Alternativ kann es auch eine serielle Verbindung sein, oder ein einfaches Socket-Protokoll auf der Basis weniger Daten. Dann ist das Proxy ein getrenntes Gerät, was aber softwareseitig mit den Reflection-Informationen des Target aktualisiert wird. Das Proxy kann auch auf einem nicht stationärem PC (Maintenance-Notebook) laufen, muss dann aber mit den richtigen Reflectioninformationen geladen werden.
Topic:.inspc.targetProxyTarget.reqInspc.
Das Target muss über eine Kommunikationsschnittstelle verfügen, über die wenige Worte für den Reflectionzugriff übertragen werden. Im Target muss die Kommunikationsschnittstelle abgefragt werden. Das kann auch in der Hintergrundschleife erfolgen. Diese Arbeitsweise ist empfohlen, da ein Handshake-Zugriff benutzt wird, der beliebig langsam sein kann. Im Target wird dann 'nur' die Restrechenzeit genutzt und nicht etwa zusätzliche Rechenzeit für den Inspectorzugriff benötigt. Die Kommunikationsschnittstelle muss die für den Inspector gedachten Daten ablegen und abholen. Das ist speziell zu programmieren. Ablegen und Abholen kann in einem globalen Speicher erfolgen. Bei einer Dual-Port-Ram-Schnittstelle braucht dazu nichts programmiert zu werden, da das Ablegen und Abholen im Dual-Port-Ram-Bereich bereits erledigt ist. |
Die Daten im Target müssen baumartig organisiert sein. Es muss eine main-Datenstruktur geben, von der alle anderen Daten aus erreichbar sind. Einzelne globale Variable sind ganz schlecht. Das scheint eine extreme Anforderung zu sein, da oft in einfachen Targets mit einzelnen globalen Variablen gearbeitet wird. Aber:
Die Organisation aller Daten in einer |
int x1; int array[20]; //Einzelvariable MyType data34; //oder einzelne statische struct
ist maschinentechnisch identisch mit
typedef struct Maindata_t { int x1; int array[20]; //Einzelvariable MyType data34; //oder einzelne statische struct } Maindata_s; Maindata_s data = {0};
Der Zugriff in Routinen kann unverändert global erfolgen: statt array[ix]
wird nur data.array[ix]
geschrieben. Für bestehende Sources ist das ein einfaches 'search and replace'. Man hat aber dann für die Weiterentwicklung
der Software nunmehr auch die Möglichkeit, über eine Referenz zuzugreifen und beispielsweise in Testumgebungen auch Mehrfachinstanzen
anzulegen. Der Zugriff über Zeiger ist in der Regel nicht etwa langsamer als der direkte globale Zugriff, wie man meinen könnte
(notwendige Adressrechnung). Moderne Prozessoren, auch kleine, führen dies parallel zu anderen Operationen aus. Der Zugriff
über Zeiger ist ggf. sogar schneller, da die Adresse im Maschinencode keine Voll-Adresse zu sein braucht (beispielsweise 32
bit breit) sondern es wird nur ein viel kleinerer Offset angegeben. Der Maschninenbefehl selbst wird also kürzer:
void mySubroutine(Maindata_s* data) { data->array[ix];
Ein Target, das mit einzelnen globalen Variablen arbeitet, sollte also umgeschrieben werden. Will man das nicht, dann geht auch die Anlage einer Main-Struktur, die mit den Referenzen der weiter genutzen globalen Variablen gefüllt wird:
int x1; //Globale variable bleiben bestehen: int array[20]; //Einzelvariable MyType data34; //oder einzelne statische struct typedef struct Maindata_t { int* x1; int** array; //Einzelvariable MyType* data34; //oder einzelne statische struct } Maindata_s; Maindata_s data = { &x1 , array , &data34 };
Die Reflectiongenerierung und der Zugriff kann mit diesen Zeigern umgehen.
In der Hintergundschleife oder im Kommunikationsthread muss dann zyklisch die Routine im Quellfile CRJ/Inspc/InspcTargetProxyTarget.c gerufen werden.
Topic:.inspc.targetProxyTarget.cmdAddr.
Es