Parameterübergabe ROBOPro <-> TXT Shared Library Interface (SLI) "Common Block"

Alles rund um TX(T) und RoboPro, mit ft-Hard- und Software
Computing using original ft hard- and software
Forumsregeln
Bitte beachte die Forumsregeln!
Antworten
hamlet
Beiträge: 332
Registriert: 12 Jan 2011, 21:41

Parameterübergabe ROBOPro <-> TXT Shared Library Interface (SLI) "Common Block"

Beitrag von hamlet » 16 Feb 2020, 18:47

Hallo Forum,
Nachdem ich mich über die bescheidene Performance des RoboPro/SLI Interfaces geärgert habe, ist mir heute eine herrliche Schweinerei für den effizienten Datenaustausch zwischen RoboPro und SLI in den Sinn gekommen. Nach Lernen des Nötigsten über das Linux memory mapping, vielen Versuchen und einer ganzen Reihe von "Segmentation Faults" funktioniert nun tatsächlich eine erste Version eines "RoboPro-SLI-Common-Block".

Im Prinzip legt man in RoboPro eine globale Liste (16bit Integer) an und initialisiert jeden einzelnen Eintrag darin mit einem eindeutigen Muster, z.B. dem Wert "2342". Dann ruft man die SLI Funktion ("ScanMem"), die nichts anderes tut als einen Block dieses Musters im Speicher des Prozesses "./TxtControlMain /dev/ttyO2 65000 0" zu suchen. Hat die Funktion diesen Block gefunden legt sie einfach ein globales short integer array auf die Block-Anfangsadresse. Fertig ist der Common Block. Von nun an kann man von beiden Seiten auf den gemeinsamen Speicher lesend und schreibend zugreifen, ohne die Werte einzelnd über das lahme RoboPro-SLI-Interface morsen zu müssen. Von RoboPro aus über das Listen-Interface und von der SLI Seite über das Array. Hierbei ist zu beachten, dass sich der R-Ausgang des Listenelementes natürlich nicht automatisch aktualisiert wenn aus SLI der Listeneintrag hinterrücks geändert wird. Man muss in RoboPro das Update durch erneutes Zuweisen des Index über den I-Eingang triggern. Damit kann man aber leben.

Die Startadresse des Memory Scans ist zurzeit noch hardcoded. Ich habe sie aus "/procs/xxxx/maps " mit der Process ID des "./TxtControlMain /dev/ttyO2 65000 0" Prozesses entnommen. Am Ende der memory map gibt es eine Reihe aufeinanderfolgender lesbarer Blöcke. Dort legt ROBOPro die Liste ab. Ich hab die Startadresses des ersten dieser Blöcke genommen. Das war tatsächlich das gößte Stück Arbeit dort den richtigen Block zu finden, denn wenn man versucht über erlaubten Speicher hinaus zu lesen, belohnt Linux das mit einem "Segmentation Fault". Glücklicherweise ändert sich die Adresse nach einem TXT powercycle nicht. Nach einem Firmware-Update sieht das vermutlich anders aus. Das lässt sich sicherlich noch verbessern. Irgendwann versuch ich noch, ob man auch einen Float-Common-Block realisieren kann.
Beste Grüße,
Helmut

Code: Alles auswählen

#include <stdio.h>          // for printf()
#include <unistd.h>         // for sleep()
#include <math.h>

#include <time.h>

#include "KeLibTxtDl.h"          // TXT Lib
#include "FtShmem.h"

static void __attribute__((constructor)) SharedLib_initialize(){
  printf( "########################################## libTxtTweak.so LOADED V%u\n", version);
}
static void __attribute__((destructor)) SharedLib_unload(){
  printf( "########################################## libTxtTweak.so UNLOADED \n");
}


/* static variables
 * note 01: The library is loaded on the first time of use and note removed from
 *          the memory after the ending of the program.
 *          So the IsInit can be true at the restart of an RoboPro program.
 * note 02: The stdout and stderr kan be see by using a SSH console and the
 *          screen command (screen -r for example
 *          https://www.computerhope.com/unix/screen.htm
 *          https://www.youtube.com/watch?v=Ernqw56u7PE
 */


static uint32_t tmDiff_us(const timespec& start, const timespec& end){
	uint32_t s = end.tv_sec-start.tv_sec;
	uint32_t us = end.tv_nsec-start.tv_nsec;
	if ( end.tv_nsec < start.tv_nsec ) {
		--s;
		us += 1000*1000*1000;
	}
	us += 500;
	us /= 1000;
	us += s * 1000*1000;
	return us;
}

extern "C" {

  // Return value:
  //  0: success, continue with waiting for pFinishVar becoming 1
  //  1: not finished
  //  2: busy (entity locked by other process)
  // -1: error
  // Other positive values can be used for other waiting codes
  // Other negative values can be used for other error codes

  short* comBlk = 0;

  int scanMem( short pattern){
    // setup timers for interrupting this scan
    // if it takes too much time
    timespec tmStart;
    timespec tmAct;
    clock_gettime( CLOCK_MONOTONIC, &tmStart);

    // got this start address from "cat /proc/xxxx/maps" for process "./TxtControlMain /dev/ttyO2 65000 0"
    // just took the the last readable blocks, non readable blocks trigger a segmentation fault
    short* const adrStart = (short*)0xb67d6000;
    static const short* adrLast = adrStart;     // static address buffer for interrupted call
    const short* adrScan = adrLast;		// current scan address
    short* adrCand = 0;				// address candidate for the common block ROBOPro list

    UINT32 patternCnt = 0;
    static UINT32 patternMaxCnt = 0;
    bool found = false;
    do{
	printf("READ %8X -> %4hX\n", adrScan, *adrScan);
	for(int i=0; i<0x1000; ++i){
	    if( *adrScan==pattern){
		printf("PATTERN (%i) FOUND at %8X %u\n", pattern, adrScan, patternCnt);
		if( !patternCnt) adrCand = (short*)adrScan;
		++patternCnt;
		if( patternCnt > patternMaxCnt) patternMaxCnt = patternCnt;
	    }else{
		if(patternCnt > 5){
		    found = true;
		    break;
		}else{
		    patternCnt = 0;
		}
	    }
	    ++adrScan;
	}
	clock_gettime( CLOCK_MONOTONIC, &tmAct);
    }while( !found && (tmDiff_us( tmStart, tmAct)<1000 || patternCnt));

  int retVal = 1;
  if( found){
      adrLast = adrStart;
      retVal = 0;
      patternMaxCnt = 0;
      printf("FOUND adr=%X cnt=%d \n", adrCand, patternCnt);

      comBlk = adrCand;

  }else{
      adrLast = adrScan;
      retVal = 1;
      printf("SCAN scn=%X max=%i \n", adrScan, patternMaxCnt);
  }
  return retVal;
  }

  int read5( short* val){
    if( !comBlk) return -1;
    *val = comBlk[5];
    printf("Read %hi from comBlk[5]\n", *val);
    return 0;
  }

  int write5( short val){
    if( !comBlk) return -1;
    comBlk[5] = val;
    printf("Wrote %hi to comBlk[5]\n", val);
    return 0;
  }

  int doom( short* val){
    printf("######################## Eject!\n");
    exit(0);
    return 0;
  }

} // extern "C"
CommonBlock.PNG
CommonBlock.PNG (30.45 KiB) 567 mal betrachtet

richard.kunze
Administrator
Beiträge: 558
Registriert: 26 Dez 2015, 23:49
Wohnort: Rhein-Main-Gebiet

Re: Parameterübergabe ROBOPro <-> TXT Shared Library Interface (SLI) "Common Block"

Beitrag von richard.kunze » 16 Feb 2020, 20:17

Wow - auf die schräge Idee muss man erstmal kommen. Klasse Hack!

Benutzeravatar
ThanksForTheFish
Beiträge: 502
Registriert: 03 Nov 2010, 21:00
Wohnort: 30900 Wedemark

Re: Parameterübergabe ROBOPro <-> TXT Shared Library Interface (SLI) "Common Block"

Beitrag von ThanksForTheFish » 16 Feb 2020, 22:55

Ach wie schön.
Kurz, knapp, schnell, effektiv. C halt.

LG, Ralf
Kommt alle gesund und virenfrei durch die Corona-Krise!

Benutzeravatar
steffalk
ft:pedia-Herausgeber
Beiträge: 1321
Registriert: 01 Nov 2010, 16:41
Wohnort: Karlsruhe
Kontaktdaten:

Re: Parameterübergabe ROBOPro <-> TXT Shared Library Interface (SLI) "Common Block"

Beitrag von steffalk » 17 Feb 2020, 11:38

Tach auch!

Das liefe bei mir je nach Tageslaune unter "Grauenhafter Hack, aber saucool!" oder unter "Cooler Hack, aber grauslich!" ;-) C halt. ;-)

Gruß,
Stefan

Antworten