German Http Request zu SPS - xml-String

Vansdan

Member
Licensed User
Longtime User
Hallo liebes Forum,

ich bin blutiger Anfänger bei Basic4Android, habe aber schon diverse Tutorial durchgemacht und möchte jetzt meine erste eigene App erstellen.

Wie der Titel schon sagt, möchte ich einen Http-Request an eine Steuerung von Phoenix Contact senden, von der ich dann die aktuell auf der Steuerung anliegenden Werte in einem Response erhalte.
Der Request wird dabei über einen xml-String ausgeführt, der folgende Syntax hat:

<body><version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver><item_list_size>2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1</n></i></item_list></body>

Die fett markierten Abschnitte sind hierbei die Variablen. Die Antwort kommt ebenfalls als xml-String, hier ist im Anschluss an die Variable noch der Wert beinhaltet. Dies schaut dann beispielsweise so aus:

<body><version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver><item_list_size>2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n><v>0</v></i><i><n>@GV.ONBOARD_INPUT_BIT1</n><v>1</v></i></item_list></body>

Die URL lautet hierbei so:
http://192.168.0.2/cgi-bin/ILRReadValues.exe

Jetzt zu meiner Frage. Könnte mir einer gutes Beispiel für meine Fragestellung zeigen bzw. wie bekomme ich die Verbindung zu meiner SPS am geschicktesten hin?

Jeder Hinweis ist gerne willkommen!

Danke und schöne Grüße
Daniel
 

Tron

Member
Licensed User
Longtime User
Http / XML

Hallo Daniel.
Am einfachsten orientierst Du dich an dem Beispiel des Twitter Feed Readers.
http://www.b4x.com/forum/basic4andr...rials/7618-twitter-feed-reader.html#post43332
und http Library Tutorials / XMLsax Tutorials
http://www.b4x.com/forum/additional...y-v1-2-head-delete-put-methods.html#post87339
http://www.b4x.com/forum/german-tutorials/7816-xml-parsing-mit-der-xmlsax-library.html#post44282
http://www.b4x.com/forum/german-tut...droid-webservices-ganz-einfach.html#post50929
Die Beiträge zeigen den Umgang mit http / XML(lesen).

Mein RSS Feed reader Experiment
erwähnenswert ist, das Ereignisgesteuerte XML parsen und das asynchrone Beschaffen der HTTP Request-Daten !
Soll heißen:
Das Ergebnis ist NICHT zum Zeitpunkt: nach "HC.Execute(req, 1)" verfügbar. Deshalb habe ich das XML erstmal auf die SD-Karte "Rss.txt" geschrieben.
Die XML ID-Tags, die Du haben möchtest, mußt Du im Coding (Sub Parser_EndElement) noch anpassen!

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   Dim STATE As Map
   Dim HC As HttpClient
   Dim PARSER As SaxParser

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   
   Dim URL As String
   URL = "http://192.168.0.2/cgi-bin/ILRReadValues.exe"   


   Dim Title, Link, Description, pubDate As String
'   txt result LIST
   Dim TxtList As List
'   Dim SRLview As ScrollView
   Dim ColDrw As ColorDrawable
End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
      PARSER.Initialize 
   End If

'   Activity.LoadLayout("2")
   HC.Initialize("HC")




End Sub

Sub Activity_Resume
   Dim req As HttpRequest

   TxtList.Initialize

   req.InitializeGet(URL)
   HC.Execute(req, 1)

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub hc_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
    Log("Error connecting: " & Reason & " " & StatusCode)
    If Response <> Null Then
        Log(Response.GetString("UTF8"))
        Response.Release
    End If
End Sub 

Sub hc_ResponseSuccess (Response As HttpResponse, TaskId As Int)
'   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirInternalCache, "Rss.txt", False), True, TaskId)
   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirDefaultExternal , "Rss.txt", False), True, TaskId)
End Sub

Sub XMLResponse_StreamFinish (Success As Boolean, TaskId As Int)
   Dim In As InputStream
   
   If Success = False Then
        Msgbox(LastException.Message, "Error")
        Return
    End If

'   parse the xml file
   In = File.OpenInput(File.DirDefaultExternal, "Rss.txt")
   PARSER.Parse(In, "Parser")
   In.Close

' TEST TEST TEST TEST TEST TEST TEST TEST 
   File.WriteList(File.DirDefaultExternal, "List.txt", TxtList)

End Sub 

Sub Parser_StartElement (Uri As String, Name As String, Attributes As Attributes)

End Sub

Sub Parser_EndElement (Uri As String, Name As String, Text As StringBuilder)
   If PARSER.Parents.IndexOf("channel") > -1 Then
      If Name = "title" Then
         Title = Text.ToString
      Else If Name = "link" Then
         Link = Text.ToString
      Else If Name = "description" Then
          Description  = Text.ToString
      Else If Name = "pubDate" Then
         pubDate = Text.ToString
      End If
   End If

   If Name = "item" Then
'   EditText1.Text = EditText1.Text & Title & CRLF & Link &CRLF& Description & CRLF _
'                & pubDate & CRLF & CRLF
   TxtList.add(Title)
   TxtList.add(Link)
   TxtList.add(Description)
   TxtList.add(pubDate)

   
   End If
End Sub


gruß Jens
 
Last edited:

Vansdan

Member
Licensed User
Longtime User
Hallo Jens,

schon mal vielen Dank für deine Hilfe. Ich schau mir das mal an und melde mich dann bei Bedarf nochmals bei dir...

Schöne Grüße
Daniel
 

Vansdan

Member
Licensed User
Longtime User
HTTP Post Request benötigt?

Hallo Jens,

ich nochmal. Habe mir jetzt dein Beispiel nochmal angeschaut, werde aber nicht ganz schlau daraus.

Nun zu meiner eigentlichen Frage. Da ich zuerst einen Request an die Steuerung senden muss (Steuerung ist gleich Webserver), um ein Ergebnis zu erhalten, wäre es hier nicht notwendig, einen Post Request anstelle eines Get Requests abzusenden? Oder verstehe ich hier etwas falsch.
Somit müsste ich als Inputstream diesen String an die URL anhängen:

<body><version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver><item_list_size>2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1</n></i></item_list></body>

Wie könnte ich dies am Besten machen und vor allem, wie finde ich die Größe (size) dieses Strings heraus?

Ich hoffe mal, dass dies einigermaßen verständlich war...

Schöne Grüße
Daniel
 

Tron

Member
Licensed User
Longtime User
Ok, hier dann ein Beispiel zum Post Request:
(siehe auch die Beispiele "Schreiben in MYSQL Datenbanken hier im Forum)
hat Dein XML-String auch einen HEADER ??

B4X:
Sub ExecuteRemoteQuery(Query As String, TaskId As Int)
   Dim req As HttpRequest
   req.InitializePost2("http://192.168.216.22/dbtest/get.php",   Query.GetBytes("UTF8"))
   hc.Execute(req, TaskId)
End Sub
Sub hc_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
   Log("Error: " & Reason & ", StatusCode: " & StatusCode)
   If Response <> Null Then
      Log(Response.GetString("UTF8"))
      Response.Release
   End If
   ProgressDialogHide
End Sub

gruß Jens
 

Vansdan

Member
Licensed User
Longtime User
xml Header

Hallo Jens,

vielen dank für deine Antwort. Die xml hat einen Header, original schaut sie so aus:

PHP:
<?xml version="1.0" encoding="UTF-8"?>

<body>
   <version>1.0</version>
   <client>IMasterPhoenix5_14_05</client>
   <client_ver>5.14.0501</client_ver>

   <item_list_size>2</item_list_size>
   <item_list>
      <i><n>@GV.PLCMODE_ON</n></i>
      <i><n>@GV.ONBOARD_INPUT_BIT1</n></i>
   </item_list>
</body>

Die Verbindung zur SPS wurde bereits mit anderen Programmen (AutoIt, Delphi) programmiert (leider nicht von mir) und der Request sowie das Auslesen der Variablen funktioniert soweit.

siehe: ILC 150 ETH Webserver

Jetzt wollen wir dieses in eine App umsetzen, um die Werte direkt auf ein Smartphone bzw. Tablet zu bekommen.

Wenn ich das richtig sehe, muss ich zuerst über Socket eine Verbindung zum Webserver der SPS herstellen und anschließend den xml String an die bereits genannte URL senden. Von dieser bekomme ich dann per Response die aktuellen Werte zurück...

Ich werde heute Abend deinen Vorschlag mal ausprobieren. Solltest du noch andere Ideen haben, nur her damit :)

Grüße
Daniel
 

Tron

Member
Licensed User
Longtime User
Naja,
bis auf Socket-Ebene muß nicht sein, HTTP reicht, da das HTTP-Protokoll ja vollständig abgebildet wird.
siehe auch http://www.b4x.com/forum/basic4andr...httputils2-web-services-now-even-simpler.html

Der XML-Header (müsste) erforderlich sein, damit saxxml das Dokument als XML erkennt.
Vorschlag:

1.Step : Senden und empfangen mit der SPS sicherstellen(via HTTP Post Kommandos mit der HTTP Lib).

wenn die Kommunikation läuft:
2.Step : Auswerten der empfangenen Daten (XML) über den Parser.

Die Ergebnisse werden ja im ersten Codingbeispiel jeweils in Textdateien geschrieben.:)
(Ok, man kann auch LOG benutzen, wenn das Ergebnis nicht allzu groß ist)
lg Jens
 
Last edited:

Vansdan

Member
Licensed User
Longtime User
Hallo Tron,

vielen Dank für die Hilfe. Mit folgendem Code bekomme ich einen Request mit dazugehöriger Antwort:

B4X:
Sub Activity_Create(FirstTime As Boolean)
   Dim job2 As HttpJob
   
   
   Dim su As StringUtils
   Dim CSV As List
   Dim Headers As List
   Dim InsertBefore As Int
   Dim NewString(5) As String
   
   
   Dim Table As List
   
   Activity.LoadLayout("layout1")
   
   'Table = su.LoadCSV2(File.DirAssets, "pdd.csv", ",", Headers)
   
   Table = File.ReadList(File.DirAssets, "pdd.csv")
      For i = 0 To Table.Size-1
      Log(Table.Get(i))
      Next


   'Send a POST request
   job2.Initialize("Job2", Me)
   job2.PostString("http://192.168.3.150/cgi-bin/ILRReadValues.exe", "<body><version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver><item_list_size>2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>Main.Test1</n></i></item_list></body>")

   '$aValues = _StringBetween($sXMLResponse, "</n><v>", "</v></i>")








End Sub

Sub JobDone (Job As HttpJob)
   Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
   If Job.Success = True Then
      Select Job.JobName
         Case "Job2"
            'print the result to the logs
            Log(Job.GetString)
      End Select
   Else
      Log("Error: " & Job.ErrorMessage)
      ToastMessageShow("Error: " & Job.ErrorMessage, True)
   End If
   Job.Release
End Sub

Schon mal danke dafür. Jetzt noch eine andere Frage. Die Variablen, die ich auswerten bzw. bearbeiten will, sind mit dazugehörigem Datentyp in einer csv-Datei gespeichert. Diese würde ich nun gerne hernehmen, und aus den Variablennamen meinen String für die Anfrage an die SPS zu generieren. Dies habe ich bisher so probiert:

B4X:
Sub Activity_Create(FirstTime As Boolean)
Dim su As StringUtils
Dim Table As List
'Datei, die ausgewertet werden soll
'Name,Data Type -> Header
'@GV.Input_Analog,WORD
'@GV.Output_Analog,WORD
'@GV.TestOutputs,BOOL
'@GV.PLCMODE_ON,BOOL
'@GV.ONBOARD_INPUT_BIT1,BOOL
'@GV.Test_BOOL,BOOL
'Main.Q_Bool,BOOL
'Main.V001,BOOL
'Main.Test1,BOOL
'Main.V005,REAL
'Main.V004,String(80)

Table.Initialize
Table = su.LoadCSV(File.DirAssets, "pdd.csv", ",")
Log (Table.Size)
Dim sColumn() As String 'Added for debugging
Dim i As Int


For i = 1 To (Table.Size)
   sColumn = Table.Get(i) 
    Log(sColumn(0)) '=Name
   Log(sColumn(1)) '=Data Type
Next

End Sub

Wie kann ich die Variablennamen sowie den Datentyp für spätere Operation in ein Array oder dergleichen zwischenspeichern? Hast du hierzu eine Idee oder Tutorial?

Danke und schöne Grüße
Daniel
 

Vansdan

Member
Licensed User
Longtime User
Lösung gefunden?

Hallo,
habe nach längerer Suche im Forum diesen Lösungsansatz gefunden:

B4X:
   Dim su As StringUtils
   Dim CSV As List
   
    Dim i, j, ColCount, RowCount As Int
   Dim Table As List
   

   

    Table = su.LoadCSV(File.DirAssets, "pdd.csv", ",")
    RowCount=Table.Size
    ColCount=2
    Dim MyArray(RowCount, ColCount), aRow() As String
    For i=0 To RowCount-1
        aRow=Table.Get(i)
        For j=0 To ColCount-1
            MyArray(i, j)=aRow(j)
         Next
         Log(MyArray(i, 0))
    Next
    Log(MyArray(6, 0))

Bis jetzt funktioniert alles damit...

Grüße
Daniel
 

Tron

Member
Licensed User
Longtime User
Soweit, so gut

Sehr gut,
hier übrigens mein Lösungsvorschlag (getestet) ist halt ein älteres Verfahren.

Ein List-Objekt zu verwenden halte ich auch für geeignet, aber bedenke das eine Aktivity Ihre Variableninhalte verliert, wenn man z.B. das Display dreht, oder eine andere Applikation startet. Aus diesem Grund sollte das Initalisieren
in der Sub Activity_Resume erfolgen, damit bei erneutem Aufruf der aktuelle Speicherstand wieder hergestellt wird !
Als Speicherort während der Testphase empfehle ich die SDkarte, damit man auch extern auf die Daten zugreifen kann.

Die Länge eines Strings war eine Frage : i = <stringname>.length
Unbedingt den Beginners Guide lesen !

Ich weiß eigentlich nicht warum die Datentypen mit abgespeichert werden sollen, da alle Werte im XML ja zuerst mal Text sind.
Das Casting z.B. eines Textes zu einem Integer erfolgt durch einfaches Zuweisen.
Der Datentyp der Liste ist intern übrigens OBJEKT, kann also alles sein.

Was ergibt das Ganze eigentlich ? Ein Cockpit oder Fernsteuerung ?

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   
   Dim HC As HttpClient

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then
      HC.Initialize("HC")
   End If

End Sub

Sub Activity_Resume
Dim Query As String 

Query = "<?xml version=" & Chr(34) & "1.0" & Chr(34) & "encoding=" & Chr(34) & "UTF-8" & Chr(34) & "?>" & _
"<body><version>1.0</version><client>IMasterPhoenix5_14_05" & _
"</client><client_ver>5.14.0501</client_ver><item_list_size>" & _
"2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1" & _
"</n></i></item_list></body>"

Send2SPS(Query,1)


End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Sub HC_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int )
   Log("Error: " & Reason & ", StatusCode: " & StatusCode)
   If Response <> Null Then
      Log(Response.GetString("UTF8"))
      Response.Release
   End If

End Sub
Sub Send2SPS(Query As String, TaskId As Int)
   Dim Req As HttpRequest
   ProgressDialogShow("connecting...")

   Req.InitializePost2("http://192.168.216.22/spstest/track.xml", Query.GetBytes("UTF8"))
   HC.Execute(Req,TaskId)

   ProgressDialogHide
End Sub

Sub HC_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   Dim Res As String

   Res = Response.GetString("UTF8")
   Log("Response from server: " & Res)

   Select TaskId
   Case 1
      Msgbox(Res,"Result") ' fail = -1

   End Select   
   
   
   Response.Release

End Sub

000.png



lg Jens
 
Last edited:

Vansdan

Member
Licensed User
Longtime User
Mein bisheriger Code

Hallo Jens,
vielen Dank für deine Antwort, werde ich mir auch mal anschauen und ausprobieren. Anbei findest du meinen bisherigen Code. Wie du ja schon festgestellt hast, fehlen mir noch einige Grundlagen, die ich mir hoffentlich noch durch Ausprobieren und eurer Hilfe aneignen werde...

Vielleicht kannst du dir meinen bisherigen Cod mal anschauen. Ich habe leider noch keinen Plan, wie ich z. B. das Zusammensetzen des POST Strings in ein Sub unterbringe und anschließend woanders wieder aufrufen kann.

Zu deiner Frage wegen den Datentypen. Es soll später möglich sein, nicht nur Variablen zu lesen sondern auch zu schreiben. Hierbei muss dann entsprechend des Datentypes eine Unterscheidung stattfinden, wie die Daten gesendet werden sollen.

Anbei der bisherige Code, Verbesserungsvorschläge erwünscht :)

B4X:
'Activity module
Sub Process_Globals
   Dim HC As HttpClient
    

End Sub

Sub Globals

Dim Spinner1 As Spinner
Dim ListView1 As ListView
End Sub

   
Sub Activity_Create(FirstTime As Boolean)
   Dim job2 As HttpJob
   Dim su As StringUtils
   Dim i, j, ColCount, RowCount, AnzV As Int
   Dim Table, CSV As List
   Dim XMLString As String
   Dim sb, sbXML As StringBuilder
   
   'Laden von layout1
   Activity.LoadLayout("layout1")
   
   'Variablennamen und Datentyoen aus pdd.csv auslesen und in MyArray schreiben
   Table = su.LoadCSV(File.DirAssets, "pdd.csv", ",")
    RowCount=Table.Size
   'Anzahl der Variablen -> item_list_size
   AnzV = RowCount-1 
    ColCount=2
    Dim MyArray(RowCount, ColCount), aRow() As String
    For i=0 To RowCount-1
        aRow=Table.Get(i)
        For j=0 To ColCount-1
            MyArray(i, j)=aRow(j)
         Next
      Next
    
   'Zusammensetzen des xml-Strings für den HTTP Request
   sb.Initialize
   For i = 1 To RowCount-1
    sb.Append("<i><n>").Append(MyArray(i, 0)).Append("</n></i>")
   Next
      
   sbXML.Initialize
   sbXML.Append("<body><version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver><item_list_size>").Append(AnzV).Append("</item_list_size><item_list>").Append(sb).Append("</item_list></body>")
   Log("Der Request-String lautet folgend:")
   Log(sbXML)
   
   'Send a POST request
   job2.Initialize("Job2", Me)
   job2.PostString("http://192.168.3.150/cgi-bin/ILRReadValues.exe", sbXML)
   '$aValues = _StringBetween($sXMLResponse, "</n><v>", "</v></i>")

End Sub

Sub JobDone (Job As HttpJob)
   Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
   If Job.Success = True Then
            'print the result to the logs
            Log(Job.GetString)
      Else
      Log("Error: " & Job.ErrorMessage)
      ToastMessageShow("Error: " & Job.ErrorMessage, True)
   End If
   Job.Release
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Wie du siehst, wird mein xml-String aus der csv-Datei erstellt. Je nach benötigte Variablen ändert sich dieser auch entsprechend. Diesen Teil kann man doch sicher in ein Untersub verlagern und dann aufrufen, oder? Noch eine andere Frage. Wie könnte ich im Anschluss aus dem erhaltenen String nur die Werte zwischen ""</n><v>" und "</v></i>" auslesen, entsprechend einem Befehl ähnlich wie StringBetween?

Schöne Grüße
Daniel

PS. Den Beginners Guide muss ich mir wohl nochmal anschauen :D
 

Vansdan

Member
Licensed User
Longtime User
Ziel?

Was ergibt das Ganze eigentlich ? Ein Cockpit oder Fernsteuerung ?

Ziel dieser App soll eine kleine Visualisierung zur Hausautomatisierung sein, die hier im speziellen mit der Phoenix ILC SPS kommunizieren kann.

Ähnlich wie das hier im Forum mit der WagoId Bibliothek abgehandelt wurde!

Mein Result:

attachment.php
 

Attachments

  • Result1.jpg
    Result1.jpg
    53.9 KB · Views: 582

Mathias

New Member
Hallo,

also ich bin der jenige, der das ganze in AutoIt gemacht hat und das ganze ins Rollen gebracht hat mit B4A. Den Datentyp habe ich damals mit abgespeichert damit man beim setzen der Variablen weiß, welche Werte man vergeben darf. Und dementsprechend das Error-Handling ausarbeitet, damit der User keine falsche Werte vergeben kann.


Gruß
Mathias
 

Vansdan

Member
Licensed User
Longtime User
Hallo

Hallo Matthias,

zum Glück hast du da schon ein wenig Vorarbeit geleistet, sonst würde ich da gar nicht durchsteigen...

Schön, dass du jetzt auch hier in diesem Forum bist, du weißt ja doch etwas besser über die ganzen Hintergründe Bescheid!

Schöne Grüße
Daniel
 

Mathias

New Member
Wir machen es ja als Team und ich will dich ja nicht alleine lassen.

Ich hab auch schon den Zuständigen bei Phoenix kontaktiert, ob die dazu noch Dokus haben. Aber er hat sich noch nicht gemeldet.


Gruß
Mathias
 

Vansdan

Member
Licensed User
Longtime User
Team

Da kann ich mich wenigstens auch mal etwas beim Programmieren einbringen und dir/euch auch evtl. eine kleine Hilfe sein...

Schöne Grüße
Daniel
 

Tron

Member
Licensed User
Longtime User
So..

Wie versprochen Step 2 : auswerten der SPS-Antwort mit dem XML Parser.
Die Ergebnisse sind in 2 Dateien auf der SD-Karte.

001.png


003.png


002.png


B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   
   Dim HC As HttpClient
   Dim TxtList As List
   Dim PARSER As SaxParser

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then
      HC.Initialize("HC")
   End If
      PARSER.Initialize 

End Sub

Sub Activity_Resume
Dim Query As String 

TxtList.Initialize
   
Query = "<?xml version=" & Chr(34) & "1.0" & Chr(34) & "encoding=" & Chr(34) & "UTF-8" & Chr(34) & "?>" & _
"<body><version>1.0</version><client>IMasterPhoenix5_14_05" & _
"</client><client_ver>5.14.0501</client_ver><item_list_size>" & _
"2</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1" & _
"</n></i></item_list></body>"

Send2SPS(Query,1)


End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
Sub HC_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int )
   Log("Error: " & Reason & ", StatusCode: " & StatusCode)
   If Response <> Null Then
      Log(Response.GetString("UTF8"))
      Response.Release
   End If

End Sub
Sub Send2SPS(Query As String, TaskId As Int)
   Dim Req As HttpRequest
   ProgressDialogShow("connecting...")

   Req.InitializePost2("http://192.168.216.22/spstest/track.xml", Query.GetBytes("UTF8"))
   HC.Execute(Req,TaskId)

   ProgressDialogHide
End Sub

Sub HC_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   Dim Res As String
   Dim In As InputStream

'   Res = Response.GetString("UTF8")
   Log("Response from server: " & Res)

   Select TaskId
   Case 1
'      Msgbox(Res,"Result") ' fail = -1
'   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirInternalCache, "Rss.txt", False), True, TaskId)
   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirDefaultExternal , "sps.xml", False), True, TaskId)

   End Select   
   
   

End Sub
Sub XMLResponse_StreamFinish (Success As Boolean, TaskId As Int)
   Dim In As InputStream
   
   If Success = False Then
        Msgbox(LastException.Message, "Error")
        Return
    End If

'   parse the xml file
   In = File.OpenInput(File.DirDefaultExternal, "sps.xml")
   PARSER.Parse(In, "Parser")
   In.Close

' TEST TEST TEST TEST TEST TEST TEST TEST 
   File.WriteList(File.DirDefaultExternal, "result.txt", TxtList)
   

End Sub 
Sub Parser_StartElement (Uri As String, Name As String, Attributes As Attributes)

End Sub

Sub Parser_EndElement (Uri As String, Name As String, Text As StringBuilder)

   If PARSER.Parents.IndexOf("body") > -1 Then
      If Name = "item_list_size" Then
         TxtList.add(Text.ToString)
      End If
   End If

   If PARSER.Parents.IndexOf("item_list") > -1 Then
      If Name = "n" Then
         'Items = Text.ToString
         TxtList.add(Text.ToString)
      End If
   End If

End Sub
grüß Jens
 

Vansdan

Member
Licensed User
Longtime User
Request / Response

Hallo Jens,
schon mal vielen Dank für die geleistete Hilfe. Schaut schon mal nicht schlecht aus, nur ergibt sich folgendes Problem.
Der String, den du unter Query

B4X:
Query = "<?xml version=" & Chr(34) & "1.0" & Chr(34) & "encoding=" & Chr(34) & "UTF-8" & Chr(34) & "?>" & _
"<body><version>1.0</version><client>IMasterPhoenix5_14_05" & _
"</client><client_ver>5.14.0501</client_ver><item_list_size>" & _
"[B][U][I]2[/I][/U][/B]</item_list_size><item_list><i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1" & _
"</n></i></item_list></body>"

aufführst, ist der Request und nicht die Antwort. Die Variablen liegen hier zwischen <i><n> und </n></i>. Die fett markierte 2 gibt dabei die Anzahl der Variablen des Strings an.
Im Beispiel wären dies @GV.PLCMODE_ON und @GV.ONBOARD_INPUT_BIT1.

B4X:
<i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1"</n></i>

Diese Variablen liegen in einer csv-Datei (pdd.csv), aus der diese beim Start der App geladen werden müssten. Die Anzahl der Variablen sowie die Variablennamen werden mittels oben gezeigten Request an die SPS geschickt, als Antwort bekommt man dann den Variablennamen einschließlich Wert.
Dies würde in diesem konkreten Fall so ausschauen:

B4X:
<body>
<version>1.0</version><client>IMasterPhoenix5_14_05</client><client_ver>5.14.0501</client_ver>
<item_list_size>2</item_list_size>
<item_list>
<i><n>@GV.GV.PLCMODE_ON</n><v>1</v></i>
<i><n>@GV.ONBOARD_INPUT_BIT1</n><v>0</v></i>
</item_list>
</body>

Aus diesem müssten dann wieder die Variablennamen sowie der dazugehörige Wert herausgetrennt werden. Das Ergebnis sollte dann so ausschauen:

Variable Wert
@GV.GV.PLCMODE_ON 1
@GV.ONBOARD_INPUT_BIT1 0

Deshalb habe ich auch schon mal geschaut, ob es eine ähnliche Funktion wie StringBetween in B4A gibt. Mit dieser könnte man dann die Variablennamen sowie den dazugehörigen Wert in eine Liste oder Array schreiben.

Schöne Grüße
Daniel
 
Last edited:

Tron

Member
Licensed User
Longtime User
So, korrigiert

Für das Laden der Variablen und deren Datentypen , habe ich ein MAP objekt verwendet.

Die Antwort der SPS:
005.png


Das Ergebnis nach dem Parsen:
004.png


Die Variablen/Typen Datei:
(kann man aber eigentlich auch im Programm direkt erzeugen)
006.png



B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
   
   Dim HC As HttpClient
   Dim PARSER As SaxParser

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim TxtList As List
   Dim SPSDataTypes As Map
End Sub

Sub Activity_Create(FirstTime As Boolean)

   If FirstTime Then
      HC.Initialize("HC")
      PARSER.Initialize 
   End If

End Sub

Sub Activity_Resume
Dim Query As String 

TxtList.Initialize
SPSDataTypes.Initialize 

' ** Initialisiere Vars
Load_Defaults

Query = "<?xml version=" & Chr(34) & "1.0" & Chr(34) & "encoding=" & Chr(34) & "UTF-8" & Chr(34) & "?>" & _
"<i><n>@GV.PLCMODE_ON</n></i><i><n>@GV.ONBOARD_INPUT_BIT1</n></i>"
Send2SPS(Query,1)


End Sub
Sub Load_Defaults

SPSDataTypes = File.ReadMap (File.DirDefaultExternal,"SPS_Vars.csv")
' Format eg. ONBOARD_INPUT:WORD

'**Manuelles Laden der MAP
'SPSDataTypes.Put("ONBOARD_INPUT","WORD")


End Sub
Sub Activity_Pause (UserClosed As Boolean)

End Sub
Sub HC_ResponseError (Response As HttpResponse, Reason As String, StatusCode As Int, TaskId As Int )
   Log("Error: " & Reason & ", StatusCode: " & StatusCode)
   If Response <> Null Then
      Log(Response.GetString("UTF8"))
      Response.Release
   End If

End Sub
Sub Send2SPS(Query As String, TaskId As Int)
   Dim Req As HttpRequest
   ProgressDialogShow("connecting...")

   Req.InitializePost2("http://192.168.216.22/spstest/track.xml", Query.GetBytes("UTF8"))
   HC.Execute(Req,TaskId)

   ProgressDialogHide
End Sub

Sub HC_ResponseSuccess (Response As HttpResponse, TaskId As Int)
   Dim Res As String
   Dim In As InputStream

'   Res = Response.GetString("UTF8")
   Log("Response from server: " & Res)

   Select TaskId
   Case 1
'      Msgbox(Res,"Result") ' fail = -1
'   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirInternalCache, "Rss.txt", False), True, TaskId)
   Response.GetAsynchronously("XMLResponse", File.OpenOutput(File.DirDefaultExternal , "sps.xml", False), True, TaskId)

   End Select   
   
   

End Sub
Sub XMLResponse_StreamFinish (Success As Boolean, TaskId As Int)
   Dim In As InputStream
   
   If Success = False Then
        Msgbox(LastException.Message, "Error")
        Return
    End If

'   parse the xml file
   In = File.OpenInput(File.DirDefaultExternal, "sps.xml")
   PARSER.Parse(In, "Parser")
   In.Close

' TEST TEST TEST TEST TEST TEST TEST TEST 
   File.WriteList(File.DirDefaultExternal, "result.txt", TxtList)
   

End Sub 
Sub Parser_StartElement (Uri As String, Name As String, Attributes As Attributes)

End Sub

Sub Parser_EndElement (Uri As String, Name As String, Text As StringBuilder)

'*** Hole die Anzahl der Werte 
   If PARSER.Parents.IndexOf("body") > -1 Then
      If Name = "item_list_size" Then
         TxtList.add(Text.ToString)
      End If
   End If

'   If PARSER.Parents.IndexOf("item_list") > -1 Then
'      If Name = "b" Then
'      TxtList.add(Text.ToString)
'      End If
'   End If

' *** Abschnitt item_list
' *** wenn das übergeordnete Element item_list ist
If PARSER.Parents.IndexOf("item_list") > -1 Then

   
' *** wenn das übergeordnete Element n ist
   If PARSER.Parents.IndexOf("n") > -1 Then

' *** wenn das aktuelle Element b ist
      If Name = "b" Then
         TxtList.add(Text.ToString)
      End If
   End If
   
' *** wenn das übergeordnete Element v ist
   If PARSER.Parents.IndexOf("v") > -1 Then

' *** wenn das aktuelle Element i ist
      If Name = "i" Then
         TxtList.add(Text.ToString)
      End If
End If
   
   
End If
   
End Sub
gruß Jens

Ps Nachbildung der Stringverarbeitung am Beispiel des (Excel VB) Befehls TRIM LTRIM RTRIM
B4X:
Sub TrimSpaces(s As String) As String  
s = RTrimSpaces(s)
s = LTrimSpaces(s)
Return s
End Sub

Sub RTrimSpaces(s As String) As String  
Dim i As Int  

For i = s.Length - 1 To 0 Step -1    
   If s.CharAt(i) <> " " Then Exit  
   Next'i  
Return s.SubString2(0, i + 1)

End Sub

Sub LTrimSpaces(s As String) As String  
Dim i As Int

For i = 0 To s.Length - 1     
   If s.CharAt(i) <> " " Then Exit  
   Next'i  
Return s.SubString( i )

End Sub
 
Last edited:

Vansdan

Member
Licensed User
Longtime User
Besten Dank

Hallo Jens,

schon mal vielen, vielen Dank für deine Hilfe.
:sign0098:

Ich werde deinen Code morgen Abend ausprobieren und über die Ergebnisse berichten...

Schönen Abend
Daniel
 
Top