XML-Datei öffnen und bestimmten Eintrag suchen

Begonnen von Christian, 21. Dezember 2011, 18:28:21

Vorheriges Thema - Nächstes Thema

0 Mitglieder und 1 Gast betrachten dieses Thema.

Christian

Hallo,


ich bin neu hier und hoffe dass ihr mir weiter helfen könnt.

Mit Hilfe einer SPS (MOVIplc) lese ich verschiedene Motordaten aus mehreren Umrichtenr aus (Motortemperatur, Drehzahl usw...)
Diese werden in einem XML-File abgespeichert, da ich zu viele Daten habe, um Sie in einem Array zu Speichern.


Auf der OSCAT-Homepage bin ich auf den XML_READER gestoßen, der ja einzelne Tags der Datei auslesen kann. In der Dokumentation der Bibliothek ist dieser Baustein jedoch nur in Verbindung mit dem HTTP_GET Baustein verwendet, welcher den Anfang und das End der Daten angibt.

Um eine Datei zu öffnen habe außerdem den SYSTEM_FILE Baustein gefunden.

Meine Frage(n) ist (sind) nun:
1. Habe ich über haupt die richtigen Bausteine ausgesucht??
2. Wenn ja, wie kann ich die beiden Verbinden, sodass es funktioniert??


Grüße
Christian

peewit

1. du hast die richtigen bausteine ausgesucht

2. mit dem file_server liest du die xml_daten in den network_buffer ein

3. dann kannst du mit dem xml_reader die daten auswerten !


Christian

Vielen Dank für die schnelle Antwort.

Ich habe aber noch Fragen zur Anwendung der Bausteine:

1. Werden die Daten automatisch in den Buffer geladen?
2. Wie greife ich auf den Buffer zu??
    Die Variable "PT" ist ja ein Pointer auf ein Byte-Array. Um jetzt auf die Daten zugreifen zu können brauche ich ja zumindest
    die erste Adresse des Byte-Arrays. woher bekomme ich die??
3. Der Baustein XML_READER velangt ja ein Anfang und ein Ende der Daten. Im Beispiel der Dokumentation ist die mit     
    HTTP_GET.BODY_START bzw. .BODY_STOP realisiert.
    Wie würde das bei diesen beiden Bausteinen aussehen?


Grüße Christian


peewit

hallo

wenn du den file_server mit den parametern versorgst, dann liest dieser die xml_datei automatisch in den network_buffer ein

der file_server und der xml_reader arbeiten beide mit dem gleichen datenarray
dieses datenarray ist vom type network_buffer

der zugriff auf die daten erfolgt nicht über pointer auch wenn so manche namensgebungen dies vermuten lassen
sondern mit simplen buffer_array index

wenn du daten mit den file_server in den network_buffer einliest, dann sind die daten im network_buffer folgend abgelegt

TYPE NETWORK_BUFFER :

NETWORK_BUFFER.SIZE = Anzahl der im buffer enthaltenen bytes
NETWORK_BUFFER.BUFFER[index] = zugriff auf die enthaltenen byte (achtung ! beginn ist bei index 0)


xml_reader.start = 0;
xml_reader.stop = NETWORK_BUFFER.SIZE - 1;


Christian

#4
Vielen Dank für die Antwort, aber ganz klar ist mir die sache noch nicht... :(

Vielleicht erkläre ich mal was ich eigentlich machen  will und füge mal meinen bisherigen Quelltext hinzu.

Ich habe einen Funktionsbaustein FB_XMLread, den ich in meinem Programm aufrufe und einige Werte übergebe.

Inenrhalb des Funktionbausteins rufe ich sowohl den Baustein XML_READER als auch den Baustein FILE_SERVER auf.
Mein größtes Problem liegt darin, dass ich mit den internen Variablen der beider Bausteine nicht zurechtkomme.

Mein bisheriger Quellcode sieht wie folgt aus:

VAR_INPUT
   Element:STRING(255);
   Attribute:STRING(255);
   Value:STRING(255);
   yFileName:STRING;
END_VAR
VAR_OUTPUT
END_VAR
VAR
   fbXMLread: XML_READER;
   fbFileOpen: FILE_SERVER;
   Path:STRING(255);


   (*fileOpen*)
   FSD : FILE_SERVER_DATA;
   PT : NETWORK_BUFFER;
   (*xml-Read*)
   CTRL : XML_CONTROL;
   BUF : NW_BUF_LONG;


   BufSize: UINT;
END_VAR
(*VAR_IN_OUT

END_VAR*)

yFileName := CONCAT(sCurrentFilenameMonitor,'.xml');
(*Dateikonfigurationen zum öffnen*)
FSD.FILENAME := yFileName;(*Dateiname an FILE_SERVER übergeben*)
FSD.MODE:= 1;(*Datei wird für Lesezugriff geöffnet*)
FSD.OFFSET:=0;(*Kein Offset beim öffnen der Datei*)


BufSize:= PT.SIZE;

fbFileOpen();


BufSize:= PT.SIZE;



CTRL.START_POS:= 0;
CTRL.STOP_POS := BufSize-1;
CTRL.ATTRIBUTE := Attribute;
CTRL.ELEMENT:= Element;
CTRL.VALUE:= Value;
fbXMLread.CTRL:=1;
fbXMLread.BUF:= BufSize;
fbXMLread();
Ctrl_1 := fbXMLread.CTRL;


Um schnelle Antwort wäre ich sehr dankbar.

Grüße Christian

peewit

das ganze solltest du in einen ablauf verpacken (CASE OF x)

1. zuerst den file_server mit daten versorgen
2. dann auf ergebnis warten
3. xml_reader parameter übergeben
4. ergebnis verarbeiten
5. weiter in schritt 3

die rufst den file_Server auf ohne parameterübergabe ?
zyklisches berschreiben der schnittstelle bringt auch nichts
der xml_reader wird benutzt obwohl noch keine daten vorhanden sind
beachte das der xml_reader auch immer nur ein element der xml_daten pro aufruf zurückgibt
also ohne abfrage schleife etc... geht das sowieso nicht.

so einfach wie in deinem programm gehts auf keinen fall




ohne einen programmierten ablauf kommt da nichts sinnvolles raus

!!! wichtig !!!
schau dir doch mal die demo programme in der bibliothek an
dann siehst du wie man sie verwendet (z.b. yahoo_weather .... file demo bausteine)


Christian

Hallo ich bins schon wieder - auch auf die Gefahr hin dass ich nerve.


Auf die Idee mit den Beispielprogrammen hätte ich auch selber kommen können.
Dabei habe ich zwei Beispiele für den XML-reader gefunden (einmal das yahoo-Beispiel und einmal das World-Weather-Beispiel).

Das Yahoo-Beispiel ist weitaus weniger komplex und ich denke für meine Zwecke ausreichend. Außerdem habe ich mir die Schrittkette in der Dokumentation angeschaut.

Mein Baustein habe ich verändert und wollte fragen, ob man da so machen kann. Meine Meinung nach müsste das jetzt so passen:
FUNCTION_BLOCK FB_XMLread
VAR
   (*Blockinstanzen*)
   fbXMLreader:XML_READER;(*Instanz von XML_READER*)
   fbFileOpen:FILE_SERVER;(*Instanz von FILE_SERVER*)
   (*definierte Datenstrukturen*)
   CTRL:XML_CONTROL;(*Control XML-reader*)
   FSD : FILE_SERVER_DATA;(*Contol File-open*)
   BUF_O:NETWORK_BUFFER;(*Buffer für XML-open*)
   BUF_R:NW_BUF_LONG;

   (*Variablen*)
   Mode:INT;(*Hauptmodus*)
   Mode_intern:INT;(*Modus für XML-analyse*)
   index:INT;(*Index 1 für BufferArray*)
   index2:INT;(*Index 2 für BufferArray*)
   stop:INT;(*StopIndex für BufferArray*)
   CommandRead:WORD;(*Kommando für XML-read*)
   CommandOpen:BYTE;(*Kommando für XML-open*)
   BufSize:INT;(*Buffergröße*)
   path_overflow: BOOL;
   RetVat:INT; (*Rückgabewert von XML-read*)

   (*Watchdog timer*)
   watchdog:TON;
END_VAR
VAR_INPUT
   yFileName:STRING;
   Attribute:STRING;
   Element_Level1:STRING;(*suchen nach axisID und BlockType*)
   Element_Level2:STRING;(*Suchen nach BlockID*)
   Element_Level22:STRING;(*Suche nach Value*)
   Value:STRING;
END_VAR
Mode:= 000;

CASE Mode OF
   000:(*Init FileOpen*)
      Init_FileOpen();
   010:(*Init FileRead*)
      Init_XMLread();
   100:
      IF index < 0 THEN
         CTRL.BLOCK1_START:= UINT#0;
         CTRL.BLOCK1_STOP := UINT#0;
         CTRL.BLOCK2_START:= UINT#0;
         CTRL.BLOCK2_STOP := UINT#0;
         watchdog(IN:=FALSE); (*Timer stoppen*)
      END_IF
      Mode := 200;
   200:(*XML auswerten*)
      Mode_intern := 10;
      WHILE CTRL.TYP < 98 DO
         fbXMLreader(CTRL:= CTRL, BUF:= BUF_R);
         CASE Mode_intern OF
            10:
               IF CTRL.ELEMENT = Element_Level1 THEN
                  Mode_intern := 20;
               END_IF
            20:
               IF CTRL.ELEMENT = Element_Level2 THEN
                  Mode_intern := 30;
               END_IF
            30:
               IF CTRL.ELEMENT = Element_Level22 THEN
                  Value := CTRL.VALUE;
                  RETURN;
               END_IF
         END_CASE
         CTRL.COUNT := CTRL.COUNT + 1;


      END_WHILE



END_CASE

Die beiden Init-Bausteine sehen wie folgt aus:
Init_FileOpen
-----------------
REPEAT (*solnge file NICHT geöffnet ist*)
   FSD.FILENAME:= CONCAT(yFileName , '.xml');
   FSD.MODE:=1;(*vorhandene Datei zum lesen öffnen*)
   FSD.OFFSET:= 0;(*start bei Dateianfang*)
   FSD.AUTO_CLOSE := T#0s; (*kein auto close verwenden*)
   BUF_O.SIZE:= 65535;(*Maximale Länge einlesen*)
UNTIL FSD.MODE = 0
END_REPEAT
BufSize :=  SIZEOF(BUF_O.BUFFER);
Mode := 010;

Init_XMLRead
-----------------
CommandRead :=  CTRL.COMMAND;
CTRL.COMMAND := WORD#0;

CTRL.START_POS:= 0;(*Übergebe die Startposition an den XML-reader*)
CTRL.STOP_POS := BufSize-1;(*Übergebe Endposition an XML-reader*)

index:= UINT_TO_INT(CTRL.START_POS);
stop:= UINT_TO_INT(CTRL.STOP_POS);
path_overflow:= FALSE;

CTRL.TYP := 0;(*Type-Code des aktuellen elements*)
CTRL.COUNT := UINT#00;(*Elementnummer 0*)
CTRL.LEVEL := UINT#00; (*Ebene 0*)
CTRL.ATTRIBUTE := ' ';(*Keine Ergebniswerte für Attribute*)
CTRL.ELEMENT :=  ' ';(*Keine Ergebniswerte für Element*)
CTRL.PATH := ' ';(*Kein Hierarchie als Text vergeben*)
CTRL.VALUE:= ' ';(*Kein Wert*);

BUF_R:= BUF_O.BUFFER;(*Übergabe der File-Open Buffer an read-Buffer*)

watchdog.PT :=  CTRL.WATCHDOG;



Mode := 100;




Vielen Dank schonmal für die Antwort

Grüße Christian

peewit

#7
hallo

das programm so im kopf durchzutesten wird schwierig

den file_server musst du auf jedenfall immer zyklisch aufrufen, da er auch noch dann dinge machen möchte wenn du
nichts mehr von ihm erwartest (so wie in allen beispiel programmen !!!)
du wirst überall das gleiche finden, also warum etwas neues erfinden

etwas mit repeat zu machen , ist sowieso selbstmord
solange die daten nicht fertig gelesen sind, hängt deine sps in dieser schleife

sowas machen normalerweise nur pc-programmierer, weil das ist es meistens egal
aber bei einer sps ist das sehr schlecht

schau dir doch auch die demo programm zum thema dateizugriff an !





Christian

Hallo,

Zunächst... natürich hast du mit der Repeat-Sache recht gehabt, das ist grober Unfug zumal das ganze ein wenig zeitkritisch ist - das habe ich nicht bedacht.

Das mit dem Öffenen der XML-Datei hat jedoch auf anhieb super geklappt - ich bin echt begeistert und die Demo-Programme ist klasse.

Jetzt ist mir aufgefallen, dass bei einmaligem Aufrufen des Funktionsbausteines sofort der WATCHDOG zuschlägt (watchdog-time beträgt 100ms).
Initialistiert habe ich den Baustein wie im DEMO-Programm, bzw. CTRL wie in der Dokumentation 0xFF.
Außderdem sind beide NETWORK_BUFFER-Variablen (ich habe eine zum Öffnen und eine zum Lesen angelegt gleich).
An was kann das liegen??


Dabei fällt mir noch eine Frage ein: Kann der XML_READER foglendes lesen bzw. wie liest er das ein:
            <Mblock axisid="1" type="1">
Hierbei ist ja "Mblock" ein Element, "axisid" ein Attribut und "1" ein Value;  "type" ein weiteres Attribut und "1" wiederum ein Wert.

Meinem Funktionsbaustein übergebe ich 4 Variablen, für jeweils einen Teil dieses Tags. Also Eine für das Element, jeweilt zwei für ein Attribut und einen Wert.
Kann der XML-reader auch den gesamten Tag lesen??


Vielen Dank schon mal für die Hilfe

peewit

#9
hallo

freud mich wenn sich jemand mal selber über meinen xml_reader drübertraut
das war ein ziemliche arbeit bis das ding richtig lief.

------------------
erklärung der watchdog:

es kann eine unbestimmte (lange) zeit dauern bis der xml_reader etwas passendes gefunden hat und somit die interne suchschleife wieder verlässt.

damit das sps programm hier nicht unnötig belastet wird und sein echtzeitverhalten verliert, habe ich den watchdog eingebaut.

es wird ein xml element ausgewertet , und wenn das kein erwünschtes ist und die watchdog time noch nicht abgeaufen ist
wird sofort das nächste element gesucht. der xml_reader bleibt solange im baustein hängen bis dieser ein element zum übergeben gefunden hat, oder die watchdog eine unterbrechung erzwingt.

wenn du bei CTRL.WATCHDOG eine zeit vorgibst dann kannst du die zeit beschränken
dann musst du aber aufpassen denn dann bekommst du nicht immer ein element zurück sonder manchmal auch den
typ 98 (watchdog) , was aber eine ganz normale sache ist.

wenn CTRL.WATCHDOG = 0 ist , dann können natürlich die 100ms oder mehr auftreten, da ja die watchdog
inaktiv ist.

---------------------
deine frage ob das auch geht   <Mblock axisid="1" type="1">
ja natürlich

schau dir mal die doku des baustein yahoo_weather an dort habe ich eine beispiel-xml datei reingegeben
da kannst du sehen was alles vom xml_reader verarbeitet werden kann.

------------------
tip !
wenn du eine immer gleiche xml_struktur hast und die elemente immer die gleiche position haben, dann kannst
du die auswertung so machen, wie ich es beim yahoo_weather baustein gemacht habe
hier wird einfach aufgrund der elementenummer eine zuordnung gemacht.

Christian

Hi,


also ich habe meinen Feher gefunden: 0xFF ist KEIN Wort, wie es die Variable COMMAND verlangt sondern lediglich ein BYTE - ein Elend mit diesen binären Operationen ;D
Die Suche funktioniert wunderbar  :) - so kann das neue Jahr starten !!

Mein nächster Schritt wäre jetzt, zu einem späteren Zeitpunkt gezielte Daten zu verändern.
Wenn ich dich richtig verstanden habe, muss ich mir lediglich den CTRL.COUNT - Stand merken, das XML-File im WRITE-Modus öffnen und dann direkt den Werte darin Verändern und danach die Datei wieder schließen, oder??

peewit

das mit den werte ändern ist ein problem

der xml_reader kann nur elemente lesen und nicht schreiben

ich habe schon öfters darüber nachgedacht, aber dabei keine einfache lösung gefunden
man müsste dann noch unterscheiden zwischen elemente ändern , einfügen und löschen

du musst das byteoffet herausfinden wo genau im buffer die daten stehen (das ist ja momentan schon gegeben)
dann können sich die daten verkürzen oder verlängern, somit müsstest du alle nachfolgenden byte verschieben
und dann den neuen wert eintragen...
zuletzt dann den buffer wieder auf die datei schreiben (wäre auch kein problem)

keine einfache sache...

Christian

Soweit so gut...

ich habe mir da heute auch schon Gedanken gemacht und habe da auch eine Idee.

Ich verwende ja zum Öffnen der Datei den FILE_SERVER. Dieser kann ja sowohl Dateien im Read-Modus als auch im Write-Modus öffnen.

Meine Vorgehensweise sieht bislang wie folgt aus (Man sollte wissen dass das Öffnen und schreiben der Dateien nicht zyklisch ausgeführt wird, sondern über einen Systemzustand getriggert):
Funktionsblock1
1. Ich öffne die Dateien mit dem FILE_SERVER im Write-Modus
2. Ich Suche mir einen bestimmten Tag mit dem XML_READER
3. Merke mir den Offset im BUFFER
4. Ich schließe das File
---------------------------------------------------------------------------------
Ist die Abarbeitung von Funktionsblock1 abgeschlossen wird Funktionsblock2 aufgerufen:
Hier werden folgende Daten übergeben
  - Dateiname
  - Offset
  - zu schreibender Wert (Value:BYTE)
------
Funktionsblock2:
1. Ich öffne den FILE_SERVER im Write-Modus
   => PT.Offset := Offset;
   => PT.SIZE := SizeOf(Value);
  Jetzt habe ich ein Problem:
  Wie kann ich Value dem PT.BUFFER übergeben?? Bei mir kommt immer der Fehler dass BYTE nicht in ein ARRAY... OF konvertiert werden kann
---------------------------------------------------------


Kann man das generell so machen?? Wenn ich die beiden Funktionsblöcke kombiniere??


Gruß und einen guten Rutsch ins neue Jahr
Christian


peewit

du kannst es sicherlich so machen

---------
Frage: value dem buffer uebergeben !
der baustein "string_to_buffer" hilft dir hier

die länge kannst du auch so bestimmen
PT.SIZE := LEN(Value);
----------
problem ist aber wenn dein neuer wert nicht die gleiche länge wie der alte wert hat, bleiben alte zeichen über oder du überschreibst andere teile

du könntest du werte in einer fixen länge dort ablegen   Value1="0000456"
maxmale zahlengroesse mit nullen aufgefüllt, so würden sich keine datenverschiebung ergeben


Christian

Hi das hört sich super an.

Ich werds im neuen Jahr gleich mal ausprobieren wenn ich wieder im Betrieb bin.

Dir eine frohes neues Jahr. Und vielen Dank für die Hilfe.