TwinCat Load Symbol Data @camper



  • Nächstes Problem.

    Definiere ich einen Alias auf ein Intarray

    TYPE TypedefIntArray : ARRAY [0..99] OF INT;
    END_TYPE
    
    tdIntArray : TypedefIntArray;
    

    Erhalte ich im Konstruktor von DatatypeInfo die nächste Exception

    else if (dt.get<DtIds::numArrayDims>() != 0)
    {
           ADSDATACURSOR_VERIFY("%1%", name.compare(0, arrStr.size(), arrStr) == 0);
           typeSpecs.emplace<Category::array>(dt, name.substr(name.find(" OF "sv) + " OF "sv.size()));
           size = 0;
           id   = DatatypeId::blob_;
    }
    

    Beim TypedefIntArray wird ein Array erkannt aber im Namen kommt kein "ARRAY OF [" vor

    Folgende Lösung:

    else if (auto dims = dt.get<DtIds::numArrayDims>(); dims != 0)
    {
        //ADSDATACURSOR_VERIFY("%1%", name.compare(0, arrStr.size(), arrStr) == 0);
    
        if (name.find(" OF "sv) == -1)
    	  typeSpecs.emplace<Category::alias>();
        else
        {
    	 auto type = name.substr(name.find(" OF "sv) + " OF "sv.size());
             typeSpecs.emplace<Category::array>(dt, type);
                
            size = 0;
    	id   = DatatypeId::blob_;
        }
    }
    


  • Das folgende liefert nun auch eine Exception. Da ich den Typedef auf das IntArray auch als Alias angelegt habe.

    // crosslink datatypes
    
    case Category::alias:
    {
            auto it = std::lower_bound(types_.begin(), types_.end(), dt.get<DtIds::type>());
            std::get<Category::alias>(type->typeSpecs) = &*it;
            ADSDATACURSOR_VERIFY("%1%", dt.get<DtIds::size>() == it->size);
            break;
    }
    

    it->size liefert (INT) => 2
    dt.getDtIds::size() liefert (ARRAY OF INT 0-99) => 200

    Wobei ich noch nicht weiß wie lösen deshalb erst mal auskommentiert.
    Ist ja "nur" eine Überprüfung.



  • Jetzt muss ich nochmals fragen. Auch wenn camper seit 2 Monaten nicht mehr online war. Vieleicht taucht er irgendwann man wieder auf.

    Was ist bei dir ein Alias?

    Im TwinCat System ist z.B. Type OTCID definiert der auf einen UDINT verweist.

    TYPE OTCID : UDINT;
    END_TYPE
    

    in cyclecheck bekomme ich beim rekursiven aufruf einen Fehler

    SizeType cycleCheck(AdsData::DatatypeInfo* p)
    {
        ...
        switch (p->typeSpecs.index())
        {
              ...
              case Category::alias:
                    cycleCheck(std::get<Category::alias>(p->typeSpecs)); 
                             // bei "OTCID" eralte ich dann für AdsData::DatatypeInfo* p = null
                    break;
        }
    ...
    }
    


  • @booster sagte in TwinCat Load Symbol Data @camper:

    in cyclecheck bekomme ich beim rekursiven aufruf einen Fehler

    Den bekomme ich weil ich, das war nur eine "Überprüfung" ist 😕 , auskommentiert habe.

    Mist.



  • Hätte noch so viele Fragen zu deinem Code.

    1. Wieso ist das feld info_ private in AdsVarData. Wenn ich die Daten (AdsVarData) einer Variablen über den Index Operator von AdsData anfordere. Möchte ich ja genau diese Informationen haben.
    2. Wieso heißt der Typ von dem Feld SubSymbolInfo. Das sind doch nicht nur Informationen zu den Subsymbols.
    3. Am SpecType der wiederum im DataTypeInfo vorhanden ist kann ich nicht unterscheiden ob ich einen struct oder eine function block vorliegen habe. Was aber entscheidend ist. Der lesende und schreibende Zugriff darauf unterscheidet sich.


  • Noch ein Fehler:

    auto     pvoidIt     = std::lower_bound(types_.begin(), types_.end(), "PVOID"sv);
    SizeType pointerSize = pvoidIt == types_.end() || pvoidIt->name != "PVOID"sv ? 4 : pvoidIt->size;
    

    Funktionalität:
    Hier wird die Größe von Pointern ermittelt in dem man in _types nach "PVOID" sucht und dessen Größe verwendet.
    Wird der Datentype "PVOID" nicht gefunden wird die Größe 4 verwendet.

    Anmerkung:
    Keine Ahnung wie std::lower_bound in einem vector von DatatypeInfo mit einem string "PVOID" sucht. Woher weiß die Funktion dass sie nach dem Feld "name" in DatatypeInfo suchen soll. Das funktioniert zwar aber ich verstehe nicht warum.

    Problem:
    "PVOID" taucht in der Liste (types_) aber nur auf wenn auch eine Instanz von PVOID irgendwo im PLC Projekt angelegt wurde. Warum weiß ich nicht. Wurde der Typ nun nicht angelegt kann ich davon auch keine Größe ermitteln und der Defaultwert 4 wird genommen als Pointersize.

    Die Pointersize ist allerdings in TwinCat3.1 -> 8 und in TwinCat2 -> 4.

    Das führt dann hier zu einer Exception:

    ADSDATACURSOR_VERIFY(
                "%1%\nSymboldata corrupted: mismatched size\nit->size == %2%\nsym.get<SymIds::size>() == %3%",
                sym.get<SymIds::size>() == it->size, it->size, sym.get<SymIds::size>());
    

    it->size ist wie gesagt 4
    und sym.get<SymIds::size>() liefert 8

    und wie sym.get<SymIds::size>() auf seine 8 kommt habe ich nach ewigem studieren und debuggen der Funktion immer noch nicht verstanden.

    template <IdType prop>
    constexpr auto get() const noexcept
    {
            return PropertyList<tag>::map::template get<prop>(data_.data_, data_.parent());
    }
    

    ?????



  • @booster sagte in TwinCat Load Symbol Data @camper:

    Anmerkung:
    Keine Ahnung wie std::lower_bound in einem vector von DatatypeInfo mit einem string "PVOID" sucht. Woher weiß die Funktion dass sie nach dem Feld "name" in DatatypeInfo suchen soll. Das funktioniert zwar aber ich verstehe nicht warum.

    Zumindest die Anmerkung habe ich gelöst 🙂

    In DatatypeInfo sind ja die verschiedenen operatoren < > und == implementiert

    friend bool operator<(const AdsData::DatatypeInfo& lhs, std::string_view rhs) { return lhs.name < rhs; }
    ...
    


  • Wieso liefert AdsVarData::shortName() z.b. bei einer Struktur den vollen Namen
    Also z.B. "Main.MyStruct.MyVariable" und nicht nur "MyVariable" was ja dem shortName entsprechen würde und nur in einem Array bekomme ich Beispielsweise "[1]" als shortName();

    Wie bekomme ich wiederum von einem Array denn vollen Name. Wenn ich hier name() aufrufe muss ich als parameter den prefix übergeben. Was ja nicht ganz logisch ist. Das sollte die Instanz ja selber wissen.

    Aber bei einem Array wird der Name der wiederum in info_ liegt gar nicht gesetzt. Warum auch immer.



  • Weiß irgendjemand was vom @camper. Ist der hier ausgestiegen?



  • @booster Das ist duchaus normal, dass Mitglieder (auch Moderatoren) mal eine ganze Weile abwesend sind, das können Monate aber auch Jahre sein. Ich war auch schon 1-2 Jahre abwesend. Letztendlich ist es halt eine Freizeitaktivität, die schnell mal weichen muss...



  • Das ist klar dass das nur eine Freizeitbeschäftigung ist. Ich stelle ja auch keinen Anspruch auf seine Anwesenheit.
    Er hat mir mit seinem Code ziemlich viel geholfen. Nichst desto trotz funktioniert er an einigen Stellen nicht. Jetzt kämpfe ich mich halt durch den Code durch. Ihr wisst ja selber wie schwer es ist wenn es nicht sein eigener Code ist.

    Da frag man sich dann an manchen Stellen ist das nun ein BUG oder hat sich der Coder einfach was anderes dabei gedacht. Das liegt auch daran dass der Code von Camper ziemlich komplex ist und mit den Kommentaren ziemlich sparsam umgegangen wurde.

    Da wäre es einfach manchmal nicht schlecht wenn man noch ein paar Fragen zum Code los werden könnte. Ich kann zwar immer mal wieder Teile aus dem Code von Camper raus ziehen und hier im Forum fragen dazu stellen. Aber die Frage was hat camper sich dabei gedacht wird wahrscheinlich nicht immer so einfach zu beantworten sein.



  • Es scheint als wäre @camper wieder online. Würdest du mir nochmals helfen?


  • Mod

    @booster
    Kannst du kurz schreiben, ob noch alle bzw, welche Fragen oben noch relevant sind?



  • Hi camper klar kann ich gerne machen. Super das du wieder da bist.

    Habe das Programm noch etwas umbauen müssen.

    Ich liste dir hier mal noch ein paar Datentypen die du noch nicht in deinem Beispiel TwinCat Projekt hattest die zu Fehler führten.

    1.) byte_ : BYTE(1..5);
    mit der Klammer hinter dem Datentyp kann ich den Wertebereich der Variablen einschränken (aber nur zur kompiletime) die Information die über ADS im TypeInfo Feld steht ist dann "BYTE(1..5)"
    -> über die Klammer wurde in deinem Programm der Datentyp als string erkannt und gehandelt. Problem bereits gelöst

    2.) Durch kennzeichnen einen Variablen als Eingangs oder Ausgangsvariable mit %I oder %Q
    Beckhoff Hilfe
    wird diese nochmals in der obersten Liste abgebildet. Der Suchalgorithmus std::upper_bound hat dann nicht mehr funktioniert.
    Problem gelöst

    3.) TYPE Alias_Array_Int : ARRAY[1..5] OF INT; END_TYPE
    Einen Alias auf ein Array anzulegen endet darin dass im Typeinfo Feld die Info "Array [MIN..MAX] OF TYPE" nicht mehr enthalten ist. Nur noch der Name des Alias. Wurde über numArrayDims trotzem als Array erkannt. Problem über Workaround gelöst. Leider keine Informationen mehr über dimensionen des Arrays

    4.) Legt man eine Variable in einem FB im Bereich VAR_IN_OUT an wird diese als Reference angelegt.
    Die Information wird dann als suffix im Typename dann einfach genau so gekennzeichnet "MyVariable(VAR_IN_OUT)"
    Problem gelöst

    5.) Ein Pointer to Array mit konstanten als Grenzen

    VAR CONSTANT
    	minIndex:INT := 1;
        	maxIndex:INT := 20;
    END_VAR
    
    VAR
         pointer_array_constant : POINTER TO ARRAY [minIndex..maxIndex] OF BYTE;
    END_VAR
    

    endet darin das ansstatt wie sonst "POINTER TO ARRAY [1..20] OF BYTE " im TypeName nun POINTER TO "ARRAY [MININDEX..MAXINDEX] OF BYTE" steht. Und davon aber auch keine Informationen der Subsymbols über ADS bereit gestellt werden.
    Workaround mit regular expressions -> Keine Dimensions mehr anlegen wenn keine Grenzen bekannt sind.

    Frag mich nicht wieso sie diesen Quatsch machen. Die Antwort von Beckhoff auf alle meine Hinweise war das ist ein feature kein bug.
    Desweiteren wurde mir mitgeteilt das die c++ Schnittstelle in Zukunft nicht mehr supported wird. Nur noch c#.

    In c# haben sie auch einen neue Klasse implementiert die die Symbole und Datentypen aus der SPS ausliest. Darin wurden sogar schon einige meiner hingewiesenen Bugs behoben. Allerdings noch nicht alle.

    Fragen stelle ich im nächsten Beitrag um das hier mal abzugrenzen.



  • Meiner Meinung nach wäre es wohl für die Zukunft besser einen Pharser zu schreiben um die ganzen Fälle im Typename feld zu ermitteln und einfacher erweitern zu können.
    Die meiste funktionalität steckt bei dir aktuell im Konstruktor von AdsData. Nicht gerade übersichtlich.

    Aktuell habe ich noch das Problem dass du in AdsVarData nur den shortName abspeicherst ohne den prefix. Wenn ich den ganzen Namen benötige muss ich selbst den prefix mit übergeben. Das Konzept dahiner habe ich noch nicht verstanden.


  • Mod

    5.) Ein Pointer to Array mit konstanten als Grenzen

    VAR CONSTANT
    	minIndex:INT := 1;
        	maxIndex:INT := 20;
    END_VAR
    
    VAR
         pointer_array_constant : POINTER TO ARRAY [minIndex..maxIndex] OF BYTE;
    END_VAR
    

    endet darin das ansstatt wie sonst "POINTER TO ARRAY [1..20] OF BYTE " im TypeName nun POINTER TO "ARRAY [MININDEX..MAXINDEX] OF BYTE" steht. Und davon aber auch keine Informationen der Subsymbols über ADS bereit gestellt werden.
    Workaround mit regular expressions -> Keine Dimensions mehr anlegen wenn keine Grenzen bekannt sind.

    Nervig. Erst recht, falls Sachen wie POINTER TO ARRAY[FOO^[1]..BAR[2]] (mit FOO, BAR ebenfalls solchen Arrays mit unklaren Grenzen) möglich sind. Man müsste wohl tatsächlich bei allen Arrays, die unbekannte Grenzen haben, diese Grenzen notieren, und versuchen, die jeweiligen Konstanten in einem weiteren Schritt auslesen (evtl. mehrfach, falls diese Konstanten eben selbst Arrayelemente sind). Mit TwinCAT3 könnte man evtl. auch Attribute heranziehen, um die Arraygrenzen zu ermitteln (nur in einfachen Fällen, weil ja Verschachtelungen eh nicht richtig gehandhabt werden).

    @booster sagte in TwinCat Load Symbol Data @camper:

    Meiner Meinung nach wäre es wohl für die Zukunft besser einen Pharser zu schreiben um die ganzen Fälle im Typename feld zu ermitteln und einfacher erweitern zu können.
    Die meiste funktionalität steckt bei dir aktuell im Konstruktor von AdsData. Nicht gerade übersichtlich.

    Urspünglich ging es ja auch nur darum, die bereitgestellten Daten auszulesen und zugänglich zumachen. Der Einbau von Workarounds wegen Bugs war nicht geplant 🙂

    Aktuell habe ich noch das Problem dass du in AdsVarData nur den shortName abspeicherst ohne den prefix. Wenn ich den ganzen Namen benötige muss ich selbst den prefix mit übergeben. Das Konzept dahiner habe ich noch nicht verstanden.

    Hier geht es darum, unnötige Duplikate zu vermeiden (schon um nicht unnötig Speicher zu verschwenden). Ein Element einer Struktur existiert ja für jede einzelne Instanz dieser Struktur; die Definition des Elementes erfolgt aber nur einmal - dieser einzelnen Definition kann aber logischerweise dann kein vollständiger Name zugeordnet werden, sondern nur der Kurzname, anhand dessen das Element innerhalb der Struktur identifiziert wird.
    Wenn ich mich recht erinnere, war das ja mit ein Teil des Problems, dass im ursprünglichen Code, den du hattest, der Speicherverbrauch enorm war, weil bei Arrays aus Strukturen auch die einzelnen Strukturelemente für jedes Arrayelement dupliziert wurden und der vollständige Name ja u.U. recht lang sein kann.



  • @camper sagte in TwinCat Load Symbol Data @camper:

    Urspünglich ging es ja auch nur darum, die bereitgestellten Daten auszulesen und zugänglich zumachen. Der Einbau von Workarounds wegen Bugs war nicht geplant

    Nun ja. Ich sag mal so die ganze string fumelei wurde ja nur gemacht weil die ADS Schnittstelle voller bugs ist. Das alles war also sowieso schon ein workaround weil Beckhoff die meisten Informationen in dem feld Typename als string codiert und hier mehr ausnahmen drin sind als Regeln.

    @camper sagte in TwinCat Load Symbol Data @camper:

    Wenn ich mich recht erinnere, war das ja mit ein Teil des Problems, dass im ursprünglichen Code, den du hattest, der Speicherverbrauch enorm war, ...

    Ja klar lag es daran, aber ich habe ja nichts davon wenn ich einfach Informationen weglasse die ich benötige nur damit ich den Speicherverbrauch reduziere. Ich muss in meiner Liste nach einem Vollständigen Namen suchen können.


  • Mod

    @camper sagte in TwinCat Load Symbol Data @camper:

    Wenn ich mich recht erinnere, war das ja mit ein Teil des Problems, dass im ursprünglichen Code, den du hattest, der Speicherverbrauch enorm war, ...

    Ja klar lag es daran, aber ich habe ja nichts davon wenn ich einfach Informationen weglasse die ich benötige nur damit ich den Speicherverbrauch reduziere. Ich muss in meiner Liste nach einem Vollständigen Namen suchen können.

    Es wird ja gerade keine Information weggelassen: eine Liste aller Variablen kann jederzeit leicht generieren werden, es muss nur beachtet werden, dass eine einfache Schleife nicht ausreicht, sondern (zweckmäßigerweise) rekursiv gearbeitet werden muss (einige meiner simplen Testprogramme tun genau das).
    Welche konkrete Funktionalität vermisst du?



  • Unser Konzept war bisher so dass ich mir alle Symbole aus der SPS lade und nur von den benötigten Symbolen Instanzen erzeuge für den lesenden und schreibenden Zugriff.

    Für dieses erzeugen der Klassen haben wir eine Factory geschrieben die Informationen über den Datentypen des Symbols benötigt. Da wir nur zugriff auf ein paar bestimmte Datentypen benötigen ist das mit den vielen ausnahmen im typename gar nicht aufgefallen bzw. war uns auch egal.
    Das Regelwerk hat für uns ausgereicht. Unser vorhaben war eigentlich nie die Intigrität der SPS zu prüfen. Sowieso nicht über die fehlerhafte ADS Schnittstelle.

    Was natürlich nicht optimal war, war das auslesen und aufbereiten der Symbole was ja aber größenteils von Beckhoff war.
    Hier hast du mir ja sehr viel geholfen.

    Nun zu deiner Frage:
    Ich will ja keine Liste aller Variablen generieren sondern nur die Informationen von ein paar benötigten Variablen anfordern.
    Und hier möchte ich ja nicht über alles interieren.

    Aber: Ich habe nun denn fullname zum test mit in AdsData abgelegt. Und muss sagen mein Speicher steigt nicht wirklich deutlich an.
    Speichverbrauch beim Start meines Programmes : 12.000 KB
    Speichverbrauch Nach dem laden aller Symbole : 18.000 KB

    Von dem her sieht das doch gut aus.



  • Würde nun gerne die AdsVarData Instanz einer Ads Variablen in meiner Zugriffsklasse auf die Ads Variable speichern. Aber dazu natürlich keine Kopie der AdsVarData anlegen. Hätte dies jetzt als shared_ptr auf die adsVardata Instanz ablegen.

    Du hast in deinem Code keinen shared_ptr verwendet.

    Weiß jetzt nicht wie ich nun von deinen zurückgelieferten AdsVarData Objekten keine kopien ablege sondern nur Zeiger darauf.

    Kannst du mir da nochmals helfen?


Anmelden zum Antworten