ASCII in STRING umwandeln, um Werte aus einer XML-Datei auszulesen

Lex

Level-1
Beiträge
115
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
ASCII in STRING umwandeln, um Werte aus einer JSON/XML-Datei auszulesen

Hallo alle zusammen,

ich hoffe ihr könnt mein Fehlendes Wissen in der Programmiersprache ST füllen und mir bei meinem Problem weiter helfen, da ich alles in CFC nur programmiere.

Ich besitze einen DENON Verstärker der per HTTP-Befehl gesteuert und ausgelesen werden kann. Dies wird per Baustein "FbHTTP_Get" realisiert wobei ein Buffer (Buffer-Statusabfrage) mit 281 Byte-Einträgen gefüllt wird. Diese müssen per ASCII-Tabelle in Character (STRING) umgewandelt werden.

Ich habe es bereits geschafft BYTE-Einträge, die im "Buffer_Statusabfrage" in einem ARRAY enthalten sind laut ASCII-Tabelle in Characters umzuwandeln (siehe Bild "CHAR_TO_STRING).

Bild CHAR_TO_STRING:
CHAR_TO_STRING.jpg

Als kleines Beispiel: Die Byte Zahl "70" bedeutet laut ASCII-Tabelle in Character umgewandelt den Buchstaben "F".

Wie bereits weiter oben erwähnt spiegeln die 281 Byte-Einträge diese XML-Seite wieder (siehe Bild "HTTP-Ausgabe").

Bild HTTP-Ausgabe:
HTTP-Ausgabe.jpg

Ich weiß bereits das z.B. der Buffer_Statusabfrage [61] der Buchstabe "O" und der Buffer_Statusabfrage [62] der Buchstabe "N"für den Powerzustand ausgiebt. D.h. ist der Verstärker EINGESCHALTET ist das laut Bild "HTTP-Ausgabe" das Wort "ON" nach dem Wort <value> usw....

Mein vorhaben ist es jedoch auch die momentane Volume (Lautsträke) auszulesen. Bedauerlicherweise ändert sich der Array-Eintrag je nach Betriebszustand des Verstärkers. Ist der Verstärker EINGESCHALTET besitzt das "-" vor der Zahl "52.5" den "Buffer_Statusabfrage [212]", ist der Verstärker AUSGESCHALTET besitzt das "-" den "Buffer_Statusabfrage [213]". Das liegt daran das, dass Wort weiter oben (aus Bild "HTTP-Ausgabe") von ON auf OFF wechselt und somit einen Eintrag mehr einnimmt und sich es um einen Array-Eintrag verschiebt.
Im Onlinemodus sieht es wie folgt aus... ebenso sieht man eine Realisierung um den Betriebszustand (AN oder AUS) des Verstärkers festzustellen.

Bild Onlinestatus:
Onlinestatus.jpg

Wie kann ich nun zuverlässt diese Werte aus der XML-Datei (HTTP-Ausgabe) in e!COCKPIT auslesen?

Ich habe mir gedacht das ich vll. das gesamte ARRAY von "Buffer_Statusabfrage" mit meinem ST-Code (CHAR_TO_STRING) in einen "einzigen" STRING umwandel. Dann mit Hilfe des Baustein "FIND" (String Function) die erste Überschrift wie im Bild "HTTP-Ausgabe" zu sehen nach " <MasterVolume> " suche und dann mit dem Baustein "LEFT" (String Function), sieben Array-Einträge (für den Eintrag " <value> ") weiter springe, um dann immer an der korrekten Stelle zu sein, um z.B. die Laustärke auslesen zu können.

Wäre dies zu umständlich oder gibt es eine bessere Möglichkeit?
Würde mich wahnsinnig über einen ST-Code oder auch in CFC freuen, z.B. wie es programmiert werden könnte um das gesamte Array umgewandelt als ein einzigen String darzustellen.



Gruß Lex
 
Zuletzt bearbeitet:
Was die Wandlung in Strings angeht machst Du Dir das Leben meiner Meinung nach zu schwer. Der Verstärker sendet doch schon Texte. Erstell Dir Doch einfach eine Variable vom Typ STRING diese Variable füllst Du zunächst mit MEMSET mit 0, damit wenn der übertragene Text mal kürzer ist das Endezeichen vorhanden ist, dann kopierst Du mit MEMCPY den Inhalt des Puffers in den String. Nun kannst Du mit den entsprechenden Stringbefehlen durchsuchen und einzelne Teile rausziehen und dann z.B. in Zahlen umwandeln.
Ich kenne jetzt die WAGO Bibliotheken nicht, aber vielleicht gibt es da ja auch welche um XML-Dateien/Strings zu durchsuchen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Oliver,

vielen lieben Dank für die schnelle Rückmeldung!
Wenn ich es richtig verstanden habe Wandel ich laut deiner Lösung aber die Bytes nicht per ASCII-Tabelle um. Wie kann ich dann genau nach einem Eintrag wie z.B. "MasterVolume" suchen? Es sind dadurch nur Zahlen gegeben die mehrmals immer wieder vorkommen könnten.

Wäre es möglich ein Beispiel auf die schnelle zu programmieren, in ST oder am liebsten in CFC?


Gruß Alex
 
Hallo, Du hast recht, auf den ersten Blick enthält Dein ARRAY OF BYTES nur Zahlen, z.B. die Zahl 65Dez. Nur was die Zahlen bedeuten oder wie Sie interpretiert werden ist z.B. vom Variablentyp abhängig indem die 65Dez steht. Angenommen in Deinem ARRAY mit dem Namen ab8_Test, stehen die Dezimalzahlen 72, 65, 76, 76, 79. Parallel hast Du eine Stringvariable mit dem Namen s_Test und der Größe 10 angelegt die Du zunächst mit MEMSET(ADR(s_Test), 0, SIZEOF(s_Test)) mit dem Stringendezeichen von Codesys (der 0) füllst. Wenn Du jetzt die fünf "Zahlen" aus dem ARRAY mit MEMCPY(ADR(s_Test), ADR(ab8_Test), 5) in die Stringvariable kopierst und Dir dann dessen Inhalt anschaust steht da HALLO drin, ganz ohne das Du irgendetwas konvertiert hast. Schaust Du Dir dagegen den Speicherbereich der Variable an siehst Du wieder 72, 65, 76, 76, 79, und der Vollständigkeit halber noch 6 * 0 (Der Speicherbedarf eines Strings ist immer um 1 Byte größer, wegen dem Endezeichen, Siemens z.B. arbeitet hier anders).
 
Zuletzt bearbeitet:
Hallo,

Oliver macht das gleiche wie Du, nur das er gleich mehrere Zeichen umwandelt anstelle von einem.

Du verwendest da ja auch keine Tabelle, sonst hättest Du da etwas wie

Code:
if (c=65) then char_sto_string := "A"
stehen.

Sieh Dir ansonsten doch mal die Buffer Management Operationen in der Oscat Bilbliothek an, a hast Du auch eine Suchmethode, sowie die Umwandlung dabei.


Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Oliver macht das gleiche wie Du, nur das er gleich mehrere Zeichen umwandelt anstelle von einem.
Achtung!!! Klugscheißerei.
Genaugenommen wird hier nichts gewandelt, sondern "nur" unterschiedlich interpretiert/angezeigt, ähnlich wie wenn man zwischen einer Anzeige in Hex, Dezimal oder Binär umschaltet, im Speicher steht bei allen drei Varianten immer das gleiche, so auch hier .
 
Zuletzt bearbeitet:
Moin, ich war mal so frei ...
Hoffe es stört Dich nicht, wenn Du parallel auch gleich noch Mute und Co auswerten kannst ;)

Code:
FUNCTION FC_GetXmlValueMulti : ARRAY[1..10] OF STRING(20)

VAR_INPUT
    pabRxBuffer        : POINTER TO ARRAY[1..500] OF BYTE;
    udiRxNByte        : UDINT;
    asAttributeName    : ARRAY[1..10] OF STRING;
END_VAR


VAR
    udiIdx            : UDINT;
    pbChar            : POINTER TO BYTE;
    psChar            : POINTER TO STRING(1);
    sChar            : STRING(1);
    sTmp            : STRING;
    xStart, xStartM, xEnd, xIgnor, xFound, xCopyValue    : BOOL;
    iFoundIdx            : INT;
    i                : INT;
END_VAR

Code:
FOR i := 1 TO 10 DO
    FC_GetXmlValueMulti[ i ] := 'attribute not found';
END_FOR
pbChar := pabRxBuffer;


FOR udiIdx := 1 TO udiRxNByte +1 DO


    xIgnor :=pbChar^ = 16#2F;    (* '/' *)
    xStartM := pbChar^ = 16#3C;    (* '<' *)
    xEnd := (pbChar^ = 16#3E) OR (xStartM AND xCopyValue);    (* '>' *)




    IF (xStart OR xCopyValue) AND NOT xEnd AND NOT xIgnor THEN
        psChar := pbChar;
        IF pbChar^ = 16#0 THEN EXIT; END_IF    (* EOF string *)
        sChar := psChar^;
        sTmp := CONCAT(sTmp, sChar);
    ELSIF xEnd THEN
        IF xFound AND NOT xCopyValue THEN
            xCopyValue := TRUE;
        ELSIF xFound AND  xCopyValue THEN
            FC_GetXmlValueMulti[ iFoundIdx ] := sTmp;
            xFound := FALSE;
            xCopyValue := FALSE;
        ELSE
            FOR i := 1 TO 10 DO
                 IF (sTmp = asAttributeName[ i ]) AND (asAttributeName[ i ] <> '') THEN
                    xFound := TRUE;
                    iFoundIdx := i;
                    EXIT;
                END_IF
            END_FOR
        END_IF
        sTmp := '';
    END_IF


    xStart := (xStart OR xStartM) AND NOT xEnd AND NOT xIgnor;
    pbChar := pbChar +1;
END_FOR

Programmaufruf je nach Sprach in etwa so ...

Code:
    asAttributeName    : ARRAY[1..10] OF STRING := 'MasterVolume', 'VolumeDisplay', 'dummy', 'Mute', 6('');
    asAttributeValue    : ARRAY[1..10] OF STRING(20);
--------------
asAttributeValue := FC_GetXmlValueMulti(pabRxBuffer, udiRxNByte, asAttributeName);

Edit: Ist mit CODESYS 2 geschrieben, hoffe der e!C Compiler meckert nich irgendwo.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Erstmal vielen lieben DANK an @oliver.tonn , @Thruser und @KLM!

@KLM:

Genau so etwas hatte ich mir vorgestellt wie du es eigentlich schon fertig abgeliefert hast ;-)
Hätte jedoch noch Fragen zu deinem Programm:

1.) Wie genau würde es wie im meinem Bild zu sehen fertig aussehen?

FC_GetXMLValueMulti.jpg

2.)
Da ich nicht wie bereits erwähnt fit in ST bin, bräuchte ich zu paar Sachen Deklarationshilfe. Laut deinem Programmaufrufs-Code befinden sich zwei Variablen mit der selben Bezeichnung (asAttributeValue). Wie soll das funktionieren und we ist das bei dir gemeint da du diese "---------------" noch verwendet hast (als Trennlinie für etwas?).

3.)
Ebenso wollte ich nachfragen was ich wo verändern müsste um nach anderen Variablen zu suchen, für andere XML-Dateien? Bräuchte ich hier einfach nur in der Zeile bei " asAttributeName : ARRAY[1..10] OF STRING :='MasterVolume', 'VolumeDisplay', 'dummy', 'Mute', 6(''); " die Einträger "MasterVolume ; VolumeDisplay usw." ändern?

4.)
Wo müsste ich was verändern um dem Baustein (FC_GetXmlValueMulti) mit der richtigen XML-Datei zu füttern. Wird bestimmt über den Buffer laufen, aber was müsste ich wo, wie bei mir bereits Realisiert, an deinem Baustein am Eingang ändern?

Vll könntest du mir dabei nochmals helfen KLM in meiner CFC-Programmierung, oder auch jemand anderes.
Ebenso bräuchte ich Hilfe zu den drei aufkommenden Deklarationsfehlern. Wüsste nicht was falsch sein sollte.

NACHTRAG:

Habe es im nachhinein doch noch selbst geschafft. Die zwei Codes von KLM wurden in eine Function in e!COCKPIT 1:1 kopiert und anschließend wie im Bild "Offline" zu sehen als Baustein (FC_GetXmlValueMulti) im Programm aufgerufen und verknüpft.
Ich bin wahnsinnig HAPPY über eure Hilfe und vor allem deiner KLM. Das fertige Programm sieht wie folgt aus:

Offline:
XML-Offlinemodus.jpg

Online:
XML-Onlinemodus.jpg


Gruß Lex
 
Zuletzt bearbeitet:
Hi,

KLM wird wohl später noch antworten. Ich fange mal an.

Erstmal vielen lieben DANK an @oliver.tonn , @Thruser und @KLM!

2.)
Da ich nicht wie bereits erwähnt fit in ST bin, bräuchte ich zu paar Sachen Deklarationshilfe. Laut deinem Programmaufrufs-Code befinden sich zwei Variablen mit der selben Bezeichnung (asAttributeValue). Wie soll das funktionieren und we ist das bei dir gemeint da du diese "---------------" noch verwendet hast (als Trennlinie für etwas?).
die ersten beiden Zeilen sind die Variablendeklaration und kommen in das obere Fenster. Die unterste Zeile ist der Programmaufruf in ST, den kannst Du hier nicht verwenden da CFC

4.)
Wo müsste ich was verändern um dem Baustein (FC_GetXmlValueMulti) mit der richtigen XML-Datei zu füttern. Wird bestimmt über den Buffer laufen, aber was müsste ich wo, wie bei mir bereits Realisiert, an deinem Baustein am Eingang ändern?
In pabRxBuffer muß der Empfangsbuffer vom FbHTTP_Get Baustein, also auch adr(Buffer_Statusabfrage). udiRxBuffer ist die Anzahl der empfangenen Bytes ByteCount_Status.

Vll könntest du mir dabei nochmals helfen KLM in meiner CFC-Programmierung, oder auch jemand anderes.
Ebenso bräuchte ich Hilfe zu den drei aufkommenden Deklarationsfehlern. Wüsste nicht was falsch sein sollte.

Arrays werden unter e!Cockpit (Codesys 3.5) etwas anders initialisiert. Da müssen noch eckige Klammern drum.
Code:
[FONT=arial][COLOR=#333333]asAttributeName : ARRAY[1..10] OF STRING[/COLOR][COLOR=#333333]:=[[/COLOR][COLOR=#333333]'MasterVolume', 'VolumeDisplay', 'dummy', 'Mute', 6('')];
[/COLOR][/FONT]


@KLM
sollte man nicht vielleicht auch noch CR und LF mit rausfiltern?

Gruß
 
Zuletzt bearbeitet:
Vielen DANK Thruser... warst leider anscheinend schon dabei zu antworten als ich den NACHTRAG gepostet hatte, tut mir leid. Das letzte Problem mit der Dekleration (Lösung: eckige Klammer) ist somit jetzt auch Vergangenheit.

p.S.: Was bedeutet die 6(' ') eigentlich in diesere Dekleration asAttributeName : ARRAY[1..10] OF STRING := 'MasterVolume', 'VolumeDisplay', 'dummy', 'Mute', 6(''); ? (Für mich als Info)


Danke nochmals.
 
Zuletzt bearbeitet:
Wieso muss das gemacht werden? Habe es jetzt bei mir nicht deklariert und es funktioniert dennoch.
 
Hi,

es muß wahrscheinlich nicht gemacht werden, die werden automatisch mit Leerstrings gefüllt. Es kann aber mal sein, daß man andere default Werte benötigt. Außerdem ist es so sauberer von der Programmierung, indem man alle Arraypositionen besetzt. In anderen Programmiersprachen kann es zu Fehlern führen.

Wie hast Du das Array denn initialisiert?

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe es momentan so gelöst:

XML-Offlinemodus_2.jpg

Da ich denn Status des Verstärkers immer aktualisieren muss, habe ich es derzeitig mit dem Baustein "FbBlinker" gelöst. Ist das so richtig oder gibt es bessere/ Ressourcenschonendere Lösungen. Momentan ist der Blinker folgendermaßen eingestellt:

  • tTimeHigh: t#1s
  • tTimeLow: t#5s

Eigentlich müsste sich der Status erst nach einer Änderung automatisch aktualisieren und nicht alle fünf Sekunden.



Gruß Lex
 
Zuletzt bearbeitet:
Nicht vorgebene Felder werden also automatisch mit '' (Leerstring) initialisiert.

Was meinst Du mit 'nach einer Änderung'? Der Verstärker meldet sich ja nicht bei der Steuerung automatisch wenn sich etwas geändert hat, daher mußt Du regelmäßig den Status abfragen.

Ich hätte den normalen TON Timerbaustein verwendet und da als Eingang die negierte Visu_Taster_ ... _MZ_Statusabfrage. Die Variabel muß ja auf True gesetzt werden damit der HTTP Baustein anfängt zu arbeiten und wird dann wenn der Auftrag abgearbeitet ist auf False gesetzt. Da die Variable jetzt auch noch durch den Blinker Baustein gesetzt wird kann ich mir vorstellen, daß es da zu Konflikten kommen kann, je nachdem wie der Baustein arbeitet und die Variable setzt.

Gruß
 
Moin,
alles Fragen zum Quellcode scheinen geklärt. Besten Dank Thruser.
Ohne zyklische Abfrage wirst Du die Änderung nicht feststellen. Nachdem der Get FB den Trigger aber wahrscheinlich selbst wieder zurücksetzt (?) würde ich nicht mit einem Blinker arbeiten, sondern mit einem puls-generiertem Set. Also ne Kombination aus einem TON oder TOF mit Flankenerkennung. Eingang des Zeitbausteins ist dann sein eigener negierter Ausgang. Wenn Du den noch mit dem Busy AND verknüfpst vermeidest Du Trigger-Befehle ohne Wirkung.
Bei Deinem Programm wäre aber noch die Frage, ob nicht mein Praser FB einen Trigger vertragen könnte. Sonst rattert der in jedem Zyklus alle ASCII Bytes durch.

@KLM
sollte man nicht vielleicht auch noch CR und LF mit rausfiltern?
Hatte die XML nicht und hab geraten, dass da CR LF drin sind. Muss aber nicht zwingend und ggf. sind auch noch Tabs dring. Ist aber egal, weil der Praser nur auf die Anfangs- und Endzeichen <> schaut. Alles dazwischen wird nicht berücksichtigt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Danke euch beiden für die schnellen Antworten!

@KLM
Nachdem der Get FB den Trigger aber wahrscheinlich selbst wieder zurücksetzt (?)...

Ja das macht er, nach ca. 1 Sekunden.

@KLM
Hatte die XML nicht und hab geraten, dass da CR LF drin sind.

Also wenn mit LF = Line Feed gemeint ist, kann ich dies bestätigen, dass diese in der XML-Datei vorhanden sind. Sie werden im Buffer-Array als Byte (10) abgespeichert.

@KLM
Bei Deinem Programm wäre aber noch die Frage, ob nicht mein Praser FB einen Trigger vertragen könnte. Sonst rattert der in jedem Zyklus alle ASCII Bytes durch.

Aus Logischer Sicht würde ich diese Frage mit JA beantworten. Denn von meinem Wissen her ist dies immer nur Vorteilhaft und Ressourcenschonender für den Controller, oder sehe ich das falsch?

Habe es hoffentlich richtig verstanden mit dem TON-Baustein. Wäre die 1. Variante oder die 2. Variante richtig, siehe Bilder?

1. Variante:
Automatische Statusabfrage.jpg

2. Variante:
2. Variante.jpg
 
Zuletzt bearbeitet:
@KLM

Hätte noch eine letzte Frage zu deiner Funktion um die XML-Werte auszulesen.
Ich habe das gleiche Verfahren mit meinem Receiver. Dieser spuckt folgende XML-Datei aus (siehe Bild "Receiver_XML-Ausgabe").

Bild Receiver_XML-Ausgabe:
Receiver_XML-Ausgabe.jpg

Jedoch wird der gesuchte Wert leider nicht korrekt herausgefiltert. Dies liegt daran da sich folgendes in der XML-Datei gegenüber dem Denon-Receiver, zwei Werte unterhalb der Überschrift mit dem Pfeil befindet (siehe Bild "Receiver_XML-Ausgabe").
Um es besser zu verstehen gliedere ich es hier nochmal auf:
--> <e2service>
<e2servicereference> WERT 1 </e2servicereference> in dieser Zeile ist der erste Wert unterhalb der Überschrift mit dem aufgeklappten Pfeil​
<e2servicename> WERT 2 </e2servicename> in dieser Zeile ist der zweite Wert unterhalb der Überschrift mit dem aufgeklappten Pfeil​
.​
.​
.

Was müsste man in deiner Function abändern damit das Programm wie im nächsten Bild die korrekten, anstelle komischer Werte ausgibt?

Bild Receiver_XML_GesuchteWerte:
Receiver_XML_GesuchteWerte.jpg



Gruß Lex
 
Zuletzt bearbeitet:
Hallo,

habe gerade 1,5h mit dm CFC Editor gekämpft. So sollte das funktionieren, ist aber nicht getestet.

Also wenn mit LF = Line Feed gemeint ist, kann ich dies bestätigen, dass diese in der XML-Datei vorhanden sind. Sie werden im Buffer-Array als Byte (10) abgespeichert.
Ja damit ist Line Feed gemeint (CR=Carriage Return). Wird wahrscheinlich zwischen > und < auftreten, nicht zwischen < und >. Am besten Du lädst mal die XML Datei hoch, dann kann man sich das direkt ansehen. Ein Screenshot ist etwas ungünstig.

Aus Logischer Sicht würde ich diese Frage mit JA beantworten. Denn von meinem Wissen her ist dies immer nur Vorteilhaft und Ressourcenschonender für den Controller, oder sehe ich das falsch?

Habe es hoffentlich richtig verstanden mit dem TON-Baustein. Wäre die 1. Variante oder die 2. Variante richtig, siehe Bilder?
CFC_HTTP_Trig.PNG
So sollte es richtig sein. Probleme bereitet mit nur Dein Visu Taster. Der darf nur ein Impuls setzen. Habe mit der Visu noch nicht gearbeitet und kenne daher die Einstellmöglichkeiten.

Die Doku von Wago ist da leider wieder sehr dürftig bezüglich xTrigger, xBusy und xError. Von einem anderen Baustein weiß ich, daß man xTrigger setzen muß und wenn die Aufgabe erledigt ist, dann wird xTrigger zurückgesetzt. Wann dann xBusy gesetzt wird und wann xError weiß ich nicht. Bei meinem Beispiel verwende ich jetzt xTrigger.

Ich habe auch noch versucht die beingte Verwendung von FC_GetXmlValueMulti zu zeigen. Dazu mußt Du auf dem Baustein ein Rechtsklick machen und EN/ENO wählen, dann kann man die Ausführung von einer Bedingung abhängig machen. Hier jetzt Beendung des FbHTTP_Get Bausteins.

Gruß
 
Zurück
Oben