JSON-RPC über C#/VB.NET Step by Step

Hallo alle zusammen,

aus gegebenen Anlass möchte ich mit diesem Thread nochmals alles zusammentragen was zur Kommunikation mit IPS über die JSON-RPC Schnittstelle zu beachten ist.

In diesem Thread bitte keine Diskussionen oder Fragen posten. Dieser Thread dient ausschliesslich dazu das Skript in seiner aktuellen Version zugänglich zu machen ohne sich die Teile in seitenweisem Studium des Forums zusammen suchen zu müssen.

Also:

JSON-RPC-Schnittstelle über VB.NET/C# „Step by Step“

Inhalt:

#1: Inhalt und Einleitung
#2: Jetzt geht es los. Hand an die Maus
#3: Code-Erweiterung für SSL
#4: Hey what’s up IPS -> MessageSink von IPS
#5: Auswerten der Antwort -> Deserialisieren (besten Dank an Kim)
#6: Auswerten der Antwort beim MessageSink (besten Dank an Kim)
#7: Die IPS_JSONRPC Klasse
#8: VB.NET Beispielprojekt zum runterladen

#16: Inhalt und Einleitung für C#
#17: Jetzt geht es los. Hand an die Maus
#18: Auswerten der Antwort -> Deserialisieren
#19: Auswerten der Antwort beim MessageSink
#20: C# Beispielprojekt zum runterladen

Einleitung:

JSON-RPC (JavaScript Object Notation Remote Procedure Call) ist ein Protokoll zum Aufruf entfernter Methoden in Computersystemen, ähnlich wie XML-RPC (die Daten werden jedoch in JSON statt in XML gesendet).

Um die JSON-RPC Schnittstelle unter IPS nutzen zu können muss sie erst aktiviert werden.

Beispiel IP-Symcon auf Windows PC:

Das geht mit Rechst Klick auf das Tray-Icon. Danach auf „Informationen“ klicken.

Jason_RPC_C_1.png

Danach neben Fernzugriff auf „Ändern“ klicken. Dabei sollte ein sicheres Kennwort (mindestens acht Zeichen mit Sonderzeichen) vergeben werden.

Jason_RPC_C_2.png

Beispiel SymBox :

Über den Browser an der Symbox anmelden. Danach Einstellungen und Fernzugriff auswählen. Dann sollte auch hier ein sicheres Kennwort (mindestens acht Zeichen mit Sonderzeichen) vergeben werden.


Sobald der Fernzugriff aktiviert wurde, kann man die JSON-RPC mit dem Benutzernamen (Lizenz-Benutzername) und dem vorher gesetzten Kennwort erreichen.

Eine JSON-RPC Anfrage (Request):

Eine JSON-RPC Anfrage sieht bei IPS so aus:

{„jsonrpc“:„2.0“,„method“:„IPS_RunScript“,„params“:[10693],„id“:„null“}

Bestandteile eines Request-Objekts

jsonrpc -> Ein String mit dem Namen der JSON-RPC-Version. Momentan fix „2.0“
method -> Der IPS-Befehl als Zeichenkette. In unserem Beispiel „IPS_RunScript“.
params -> Die Parameter der IPS-Befehls in den runden Klammern. In unserem Beispiel die Script-ID.
id -> Ein eindeutiger Identifikator für die Nachricht. Bei IPS ist die fix „null“.

Eine JSON-RPC Antwort (Response):

Eine JSON-RPC Antwort sieht bei IPS so aus:

{„result“:true,„id“:„null“,„jsonrpc“:„2.0“}

Bestandteile eines Response-Objekts

result -> Das Rückgabeobjekt der Funktion, falls kein RPC-Fehler auftrat.
id -> Enthält den gleichen Wert wie id des Requests. In unserem Fall fix „null“
jsonrpc -> Ein String mit dem Namen der JSON-RPC-Version. Momentan fix „2.0“

Als erstes erstellen wir ein neues Projekt (Windows Forms-Anwendung) mit dem Namen „IPS_JSONRPC“.

Auf der Form haben wir folgende Elemente.

Form_aufbau.PNG

Die Namen der Elemente habe ich im Screenshot beschriftet.

Der Code

Imports (benötigter Namespace)

Imports System.Net 'Namespace für HttpWeb
Imports System.Text 'Namespace für das Encoding
Imports System.IO 'Namespace für den Stream

Die Imports kommen noch vor „Public Class Form1“.

Als nächstes öffnen wir mit einem Doppelklick auf den „btSenden“ Button die btSenden_Click Sub.

Die Variablen Deklaration:

Was wird benötigt?

Natürlich wollen wir einen „Request“ (Anfrage) verschicken und einen Response (Antwort) empfangen.
Also schreiben wir als erstes:

Dim request As WebRequest
Dim response As WebResponse

Da die Autorisierung (Benutzername und Passwort) mit übertragen wird, brauchen wir noch:

Dim authInfo As String

Des weiteren benötigen wir die IPS URI. Dafür benötigen wir:

Dim IPS_URI As String

Zu guter Letzt gibt es natürlich einen Send und Receive String:

Dim Send As String
Dim Rec As String

Nun kommt der eigentliche Code.

Als erstes erstellen wir die IPS URI. Das sieht so aus:

IPS_URI = „http://“ & tbIPS_IP.Text & „:3777/api/“

Als nächstes kommt die Autorisierung dran. Dazu schreiben wir:

authInfo = tbBenutzer.Text & „:“ & tbPassword.Text
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))

Nun haben wir alle Informationen zusammengestellt für den Request-Header. Der sieht dann so aus:

request = WebRequest.Create(IPS_URI)
request.Headers(„Authorization“) = "Basic " & authInfo
request.Method = „Post“

Als nächstes bauen wir uns den JSON-RPC Request zusammen. Wir erinnern uns an den Aufbau aus der Einleitung.

{„jsonrpc“:„2.0“,„method“:„IPS_RunScript“,„params“:[10693],„id“:„null“}

Hinter „method“ kommt der eigentliche IPS-Befehl und nach „params“ kommt alles aus den runden Klammern des IPS-Befehls.

Wenn wir die JSON-RPC nachbauen sieht das so aus:

Send = „{“ & Chr(34) & „jsonrpc“ & Chr(34) & „:“ & Chr(34) & „2.0“ & Chr(34) & „,“ _
& Chr(34) & „method“ & Chr(34) & „:“ & Chr(34) & tbBefehl.Text & Chr(34) & „,“ _
& Chr(34) & „params“ & Chr(34) & „:[“ & tbParameter.Text & „],“ _
& Chr(34) & „id“ & Chr(34) & „:“ & Chr(34) & „null“ & Chr(34) & „}“

Chr(34) sind die doppelten Anführungsstriche.

Nun habe wir alles zusammen um den Request-Stream zu versenden. Das sieht dann so aus:

Dim StreamWriter As StreamWriter = New StreamWriter(request.GetRequestStream)
StreamWriter.Write(Send)
StreamWriter.Close()

Nun kommt der zweite Teil. Empfangen des Response (Antwort). Wir wollen natürlich den Response unseres Request haben. Also kommt in der ersten Zeile:

response = request.GetResponse

Danach lesen wir den Stream ein und legen die Daten ab.

Dim StreamReader As StreamReader = New StreamReader(response.GetResponseStream)
Rec = StreamReader.ReadToEnd

Am Ende dürfen wir nicht vergessen alles zu schliessen.

StreamReader.Close()
response.Close()

Die Empfangenen Daten können nun abgelegt werden.

Receive.Text = Rec

Ein erster Test mit IPS_RunScript

Erster Test.PNG

Was braucht man an Code für den SSL-Betrieb?

Für den SSL-Betreib braucht man zwei Erweiterungen und eine Code-Änderung.

Als erstes die Code-Änderung.
Natürlich läuft der SSL-Betrieb über eine https URI.
Also wird aus der IPS URI:

Alt:

IPS_URI = „http://“ & tbIPS_IP.Text & „:3777/api/“

Neu:

IPS_URI = „https://“ & tbIPS_IP.Text & „:3777/api/“

Des weiteren gibt es einen Callback mit einer Callback-Funktion.

Der Callback sieht so aus:

ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf AcceptCert)

Die Callback-Funktion sieht so aus:

Private Function AcceptCert(ByVal sender As Object, ByVal cert As System.Security.Cryptography.X509Certificates.X509Certificate, _
ByVal chain As System.Security.Cryptography.X509Certificates.X509Chain, _
ByVal errors As System.Net.Security.SslPolicyErrors) As Boolean
Return True
End Function

Ein Test mit SSL-Betrieb:

Test mit SSL.PNG

Wozu brauche ich den Rückkanal von IPS? Ich kann doch alle Variablen über „GetValue“ auslesen.

Das ist richtig. Bis zu einem gewissen Grad ist das mit Sicherheit auch sinnvoll. Der Rückkanal von
IPS hat aber den Vorteil das dir IPS alle Änderungen ab einen bestimmten Zeitpunkt zurückliefert.
Dazu gehören nicht nur Änderungen von Variableninhalten.

Für diesen Zweck gibt es die Befehle:

„IPS_GetSnapshot“ und „IPS_GetSnapshotChanges“.

Ich benötige für mich nur den Befehl „IPS_GetSnapshotChanges“.

Beim Starten der Applikation lese ich alle benötigten Variablen gezielt ein (GetValue). Danach arbeite
ich nur noch mit „IPS_GetSnapshotChanges“.

Um den Befehl richtig zu verstehen werden wir das Prozedere mit der eben erstellten Form händisch durchexerzieren.

Mit dem Befehl wird immer ein Zeitstempel als Parameter übergeben.
Beim ersten Aufruf ist der Zeitstempel „0“. Das sieht dann so aus:

IPS_GetSnapshotChanges_erste mal.PNG

Mit der ersten Antwort kommt von IPS ein gültiger Zeitstempel. Dieser Zeitstempel (TimeStamp) wird beim zweiten
Aufruf von „IPS_GetSnapshotChanges“ verwendet. In unserem Fall ist das die 2882592. Das sieht dann so aus:

IPS_GetSnapshotChanges_jedes weitere mal.PNG

Jedes weitere mal muss der letzte Zeitstempel (TimeStamp) in der Antwort von IPS für die nächste Abfrage genommen werden.
In dem unteren Beispiel ist es die 2883787.

IPS_GetSnapshotChanges.PNG

Ich hoffe das ist soweit verstanden. :slight_smile:

Um die Antworten von IPS auszuwerten hat man zwei Möglichkeiten.

  1. Den Antwort-String händisch in seine Bestandteile zerlegen.
  2. Die Antwort von IPS in ein Objekt Deserialisieren.

Die elegantere Methode ist natürlich das Deserialisieren der Antwort durch VB.NET. Diese Möglichkeit werden wir nutzen.

Als erstes brauchen wir einen Verweis auf „System.Web.Extensions“. Dieser Verweis ist jedoch nur möglich wenn euer Projekt auf das volle NET-Framework zugreifen kann. Dazu geht ihr in die Projekteigenschaften und dort in die Lasche „Kompilieren“. Unten auf der Seite gibt es einen Knopf „Erweiterte Kompilierungsoptionen“.

In den erweiterten Kompilierungseinstellungen muss das Zielframework auf .NET Framework 4 oder höher stehen.
Die Client-Profile funktionieren hier nicht.

Nun kann ein Verweis auf „System.Web.Extensions“ gemacht werden.

Deserialisieren Verweis.PNG

Der Code

Als erstes brauchen wir eine Hilfs-Klasse mit den selben Elementen die auch in der Antwort von IPS enthalten sind. Wir erinnern uns:

Eine JSON-RPC Antwort sieht bei IPS so aus:
{„result“:true,„id“:„null“,„jsonrpc“:„2.0“}

Bestandteile eines Response-Objekts

result -> Das Rückgabeobjekt der Funktion, falls kein RPC-Fehler auftrat.
id -> Enthält den gleichen Wert wie id des Requests. In unserem Fall fix „null“
jsonrpc -> Ein String mit dem Namen der JSON-RPC-Version. Momentan fix „2.0“

Also sieht unsere Hilfsklasse so aus:

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_Request_Response
Private myresult As Object
Public Property result() As Object
Get
Return myresult
End Get
Set(ByVal value As Object)
myresult = value
End Set
End Property

Private myid As Object
Public Property id() As Object
    Get
        Return myid
    End Get
    Set(ByVal value As Object)
        myid = value
    End Set
End Property

Private myjsonrpc As String
Public Property jsonrpc() As String
    Get
        Return myjsonrpc
    End Get
    Set(ByVal value As String)
        myjsonrpc = value
    End Set
End Property

End Class

Nachdem wir unseren Verweis auf „System.Web.Extensions“ gemacht haben können wir diesen Import zufügen:

Imports System.Web.Script.Serialization 'Namespace zum De/Serialisieren

Der Import kommt noch vor „Public Class Form1“.

Bei der Variablendeklaration benötigen wir noch:

Dim jsDeserializer As New JavaScriptSerializer()

Das auswerten der Antwort sieht dann so aus:

Dim IPS_JSResponse As IPS_Request_Response = jsDeserializer.Deserialize(Of IPS_Request_Response)(Rec)

Nach dieser Zeile kann auf jedes Element der Antwort Zugeriffen werden. Das Ergebnis kann dann mit:

Receive.Text = IPS_JSResponse.result

abgefragt werden.

Diese Auswertung der Antwort gilt nicht für „IPS_GetSnapshot“ und „IPS_GetSnapshotChanges“.

Schauen wir uns eine Antwort von IPS nach einem „IPS_GetSnapshotChanges“ genauer an.

{„result“:[
{„Message“:10603,„SenderID“:39800,„TimeStamp“:3911618,„Data“:["\u0005",false,"\u0005",1392919667]},
{„Message“:10603,„SenderID“:38994,„TimeStamp“:3911619,„Data“:[false,false,false,1392919667]},
{„Message“:10804,„SenderID“:38874,„TimeStamp“:3911620,„Data“:[false]},
{„Message“:10803,„SenderID“:38874,„TimeStamp“:3911621,„Data“:[1369587830,0]},
{„Message“:11202,„SenderID“:35082,„TimeStamp“:3911622,„Data“:[1,9,2,1392919667]}
],„id“:„null“,„jsonrpc“:„2.0“}

Es ist deutlich zu sehen das „result“ ein Array ist. Dieses Array bestehend aus:

Message ->MeldungsID. Siehe Liste der Konstanten im SDK in UIPSTypes.pas
SenderID ->ID von demjenigen der die Meldung verursacht hat
TimeStamp ->Zeitstempel
Data ->die aktuellen Daten

Ein Beispiel für Message aus der UIPSTypes.pas.

// — Message Constants
// — BASE MESSAGE
IPS_BASE = 10000; //Base Message

// — VARIABLE MANAGER
IPS_VARIABLEMESSAGE = IPS_BASE + 600; //Variable Manager Message
VM_CREATE = IPS_VARIABLEMESSAGE + 1; //Variable Created
VM_DELETE = IPS_VARIABLEMESSAGE + 2; //Variable Deleted
VM_UPDATE = IPS_VARIABLEMESSAGE + 3; //On Variable Update

Also steht in Message ein Wert von 10000 + 600 + 3 = 10603 bei einer Variablenaktualisierung.

Der Aufbau von „Data“ bei einer Variablenaktualisierung:

Data":[„Hallo“,false,„Hallo“,1392919667]

Der Aufbau von „Data“ bei einer Variablenänderung:

Data":[„Hallo Martin“,true,„Hallo“,1392919667]

Die Bestandteil sind:

[„Hallo Martin“, true, „Hallo“, 1392919667]

[Neuer Wert, Wertänderung (true/false), Alter Wert, Zeitstempel]

Bei einer Variablenänderung ist Data somit ein Array bestehend aus 4 Elementen (0-3)

Bei dieser Antwort habe ich zwei Hilfsklassen. Eine Hilfsklasse für „result“ und eine Hilfsklasse für die gesamte Antwort.

Als erstes die Hilfsklasse für „result“.

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_Result
Private myMessage As Integer
Public Property Message() As Integer
Get
Return myMessage
End Get
Set(ByVal value As Integer)
myMessage = value
End Set
End Property

Private mySenderID As Integer
Public Property SenderID() As Integer
    Get
        Return mySenderID
    End Get
    Set(ByVal value As Integer)
        mySenderID = value
    End Set
End Property

Private myTimeStamp As Integer
Public Property TimeStamp() As Integer
    Get
        Return myTimeStamp
    End Get
    Set(ByVal value As Integer)
        myTimeStamp = value
    End Set
End Property

Private myData As Object
Public Property Data() As Object
    Get
        Return myData
    End Get
    Set(ByVal value As Object)
        myData = value
    End Set
End Property

End Class

Danach die Hilfsklasse für die Gesamte Antwort. Man beachte das result ein Array von der ersten Klasse „IPS_Result“ ist!!

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_SnapShot
Private myresult As IPS_Result()
Public Property result() As IPS_Result()
Get
Return myresult
End Get
Set(ByVal value As IPS_Result())
myresult = value
End Set
End Property

Private myid As Object
Public Property id() As Object
    Get
        Return myid
    End Get
    Set(ByVal value As Object)
        myid = value
    End Set
End Property

Private myjsonrpc As String
Public Property jsonrpc() As String
    Get
        Return myjsonrpc
    End Get
    Set(ByVal value As String)
        myjsonrpc = value
    End Set
End Property

End Class

Das auswerten der Antwort sieht dann so aus:

Dim IPS_JSResponse As IPS_SnapShot = jsDeserializer.Deserialize(Of IPS_SnapShot)(Rec)

Nach dieser Zeile kann auf jedes Element der Antwort Zugeriffen werden. Das Ergebnis kann dann so abgefragt werden:

'Bei einer Rückmeldung von IPS ist das Array gefüllt (mindestens ein Element)
If IPS_JSResponse.result.Length Then

'Das Array durchlaufen
For Each msg As IPS_Result In IPS_JSResponse.result

  'Wenn es eine Variablenänderung ist
  If msg.Message = 10603 Then

     'und es ist eine bestimmte Variable
     If msg.SenderID = 23491 Then
        'dann ablegen von Neuwert      und     Altwert
        Receive.Text = msg.Data(0) & vbCrLf & msg.Data(2)
     End If
  End If

Next

'den letzten Zeitstempel ablegen für die nächste Abfrage
tbParameter.Text = IPS_JSResponse.result(IPS_JSResponse.result.Length - 1).TimeStamp

Mit dem Wissen das wir jetzt haben erstellen wir eine Klasse zu Kommunikation mit IPS.
Also fügen wir unserem Projekt eine neue Klasse hinzu. Diese Klasse nennen wir IPS_JSONRPC.

Als erstes brauchen wir einen Verweis auf „System.Web.Extensions“.
Dieser Verweis sollte ja noch bestehen! Für kommende Projekte ist
der natürlich zwingend erforderlich.

Oberhalb von „Public Class IPS_JSONRPC“ kommen die Imports:

Imports System.Net 'Namespace für HttpWeb
Imports System.Text 'Namespace für das Encoding
Imports System.IO 'Namespace für den Stream
Imports System.Web.Script.Serialization 'Namespace zum De/Serialisieren

In der Klasse kommen als erstes die Variablen die von "Aussen "erreichbar sein sollen.

'Deklaration
Public TimeStamp As Integer = 0
Public MSValidData As Boolean = False

Die Klasse hat zwei Funktionen. Als erstes kommt der " IPS_Request".

Public Function IPS_Request(ByVal IPS_IP As String, ByVal IPS_User As String, ByVal IPS_Password As String, ByVal IPS_Method As String, ByVal IPS_Params As String, ByVal SSL As Boolean) As Object
    'Deklaration
    Dim request As WebRequest
    Dim response As WebResponse
    Dim authInfo As String
    Dim IPS_URI As String
    Dim Send As String
    Dim Rec As String
    Dim jsDeserializer As New JavaScriptSerializer()
    'Call-Back für SSL
    ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf AcceptCert)
    'erstellen der IPS-URI -> wenn SSL dann https
    If SSL Then
        IPS_URI = "https://" & IPS_IP & ":3777/api/"
    Else
        IPS_URI = "http://" & IPS_IP & ":3777/api/"
    End If
    'erstellen der Authorisierung
    authInfo = IPS_User & ":" & IPS_Password
    authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))
    'erstellen des Request Headers
    request = WebRequest.Create(IPS_URI)
    request.Headers("Authorization") = "Basic " & authInfo
    request.Method = "Post"
    'erstellen der JSON-RPC
    '{"jsonrpc":"2.0","method":"IPS_RunScript","params":[10693],"id":"null"}
    Send = "{" & Chr(34) & "jsonrpc" & Chr(34) & ":" & Chr(34) & "2.0" & Chr(34) & "," _
               & Chr(34) & "method" & Chr(34) & ":" & Chr(34) & IPS_Method & Chr(34) & "," _
               & Chr(34) & "params" & Chr(34) & ":[" & IPS_Params & "]," _
               & Chr(34) & "id" & Chr(34) & ":" & Chr(34) & "null" & Chr(34) & "}"
    Try
        'Den request-Stream versenden
        Dim StreamWriter As StreamWriter = New StreamWriter(request.GetRequestStream)
        StreamWriter.Write(Send)
        StreamWriter.Close()
        'Den response-Stream empfangen
        response = request.GetResponse
        Dim StreamReader As StreamReader = New StreamReader(response.GetResponseStream)
        Rec = StreamReader.ReadToEnd
        StreamReader.Close()
        response.Close()
        Dim IPS_JSResponse As IPS_Request_Response = jsDeserializer.Deserialize(Of IPS_Request_Response)(Rec)
        IPS_Request = IPS_JSResponse.result
    Catch ex As Exception
        IPS_Request = "IPS Comm. Fault"
    End Try
End Function

Danach kommt die Funktion " IPS_MessageSink".

Public Function IPS_MessageSink(ByVal IPS_IP As String, ByVal IPS_User As String, ByVal IPS_Password As String, ByVal SSL As Boolean) As Object
    'Deklaration
    Dim request As WebRequest
    Dim response As WebResponse
    Dim authInfo As String
    Dim IPS_URI As String
    Dim Send As String
    Dim Rec As String
    Dim jsDeserializer As New JavaScriptSerializer()
    'Call-Back für SSL
    ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf AcceptCert)
    'erstellen der IPS-URI -> wenn SSL dann https
    If SSL Then
        IPS_URI = "https://" & IPS_IP & ":3777/api/"
    Else
        IPS_URI = "http://" & IPS_IP & ":3777/api/"
    End If
    'erstellen der Authorisierung
    authInfo = IPS_User & ":" & IPS_Password
    authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))
    'erstellen des Request Headers
    request = WebRequest.Create(IPS_URI)
    request.Headers("Authorization") = "Basic " & authInfo
    request.Method = "Post"
    'erstellen der JSON-RPC
    '{"jsonrpc":"2.0","method":"IPS_RunScript","params":[10693],"id":"null"}
    Send = "{" & Chr(34) & "jsonrpc" & Chr(34) & ":" & Chr(34) & "2.0" & Chr(34) & "," _
               & Chr(34) & "method" & Chr(34) & ":" & Chr(34) & "IPS_GetSnapshotChanges" & Chr(34) & "," _
               & Chr(34) & "params" & Chr(34) & ":[" & TimeStamp & "]," _
               & Chr(34) & "id" & Chr(34) & ":" & Chr(34) & "null" & Chr(34) & "}"
    Try
        'Den request-Stream versenden
        Dim StreamWriter As StreamWriter = New StreamWriter(request.GetRequestStream)
        StreamWriter.Write(Send)
        StreamWriter.Close()
        'Den response-Stream empfangen
        response = request.GetResponse
        Dim StreamReader As StreamReader = New StreamReader(response.GetResponseStream)
        Rec = StreamReader.ReadToEnd
        StreamReader.Close()
        response.Close()
        'Meldungsarray von IPS Deserialisieren
        Dim IPS_JSResponse As IPS_SnapShot = jsDeserializer.Deserialize(Of IPS_SnapShot)(Rec)
        'Bei einer Rückmeldung von IPS ist das Array gefüllt (mindestens ein Element)
        If IPS_JSResponse.result.Length Then
            'und kann ausgegeben werden
            IPS_MessageSink = IPS_JSResponse.result
            MSValidData = True
            TimeStamp = IPS_JSResponse.result(IPS_JSResponse.result.Length - 1).TimeStamp
        Else
            IPS_MessageSink = vbNull
            MSValidData = False
        End If
    Catch ex As Exception
        IPS_MessageSink = vbNull
        MSValidData = False
    End Try
End Function

Danach kommt die Call-Back Funktion für den SSL - Betrieb.

'Wird benötigt für SSL (https)
Private Function AcceptCert(ByVal sender As Object, ByVal cert As System.Security.Cryptography.X509Certificates.X509Certificate, _
                        ByVal chain As System.Security.Cryptography.X509Certificates.X509Chain, _
                        ByVal errors As System.Net.Security.SslPolicyErrors) As Boolean
    Return True
End Function

Unterhab der Klasse sind noch die Hilfsklassen zum Deserialisieren.

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_Request_Response
Private myresult As Object
Public Property result() As Object
Get
Return myresult
End Get
Set(ByVal value As Object)
myresult = value
End Set
End Property

Private myid As Object
Public Property id() As Object
    Get
        Return myid
    End Get
    Set(ByVal value As Object)
        myid = value
    End Set
End Property
Private myjsonrpc As String
Public Property jsonrpc() As String
    Get
        Return myjsonrpc
    End Get
    Set(ByVal value As String)
        myjsonrpc = value
    End Set
End Property

End Class

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_Result
Private myMessage As Integer
Public Property Message() As Integer
Get
Return myMessage
End Get
Set(ByVal value As Integer)
myMessage = value
End Set
End Property

Private mySenderID As Integer
Public Property SenderID() As Integer
    Get
        Return mySenderID
    End Get
    Set(ByVal value As Integer)
        mySenderID = value
    End Set
End Property
Private myTimeStamp As Integer
Public Property TimeStamp() As Integer
    Get
        Return myTimeStamp
    End Get
    Set(ByVal value As Integer)
        myTimeStamp = value
    End Set
End Property
Private myData As Object
Public Property Data() As Object
    Get
        Return myData
    End Get
    Set(ByVal value As Object)
        myData = value
    End Set
End Property

End Class

'Hilfsklasse zum Deserialisieren der Antwort
Public Class IPS_SnapShot
Private myresult As IPS_Result()
Public Property result() As IPS_Result()
Get
Return myresult
End Get
Set(ByVal value As IPS_Result())
myresult = value
End Set
End Property

Private myid As Object
Public Property id() As Object
    Get
        Return myid
    End Get
    Set(ByVal value As Object)
        myid = value
    End Set
End Property
Private myjsonrpc As String
Public Property jsonrpc() As String
    Get
        Return myjsonrpc
    End Get
    Set(ByVal value As String)
        myjsonrpc = value
    End Set
End Property

End Class

Einbinden der Klasse in unsere Form:

Nach „Public Class Form1“ kommt:

Dim IPS_Comm As New IPS_JSONRPC

Der Request:

Private Sub btSenden_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btSenden.Click
    Receive.Text = IPS_Comm.IPS_Request(tbIPS_IP.Text, tbBenutzer.Text, tbPassword.Text, tbBefehl.Text, tbParameter.Text, cbSSL.Checked)
End Sub

Der MessageSink (Beispiel für eine Variable und Anzeige des letzten Zeitstempels):

Private Sub btStartStop_MessageSink_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStartStop_MessageSink.Click
    'MessageSink
    Dim Result As Object = IPS_Comm.IPS_MessageSink(tbIPS_IP.Text, tbBenutzer.Text, tbPassword.Text, cbSSL.Checked)
    'Bei validen Daten
    If IPS_Comm.MSValidData Then
        'umpacken in ein Array vom Typ IPS_Result
        Dim IPS_MessageSink() As IPS_Result
        IPS_MessageSink = Result
        ' Das Array durchlaufen
        For Each msg As IPS_Result In IPS_MessageSink
            'bei einer Variablenänderung
            If msg.Message = 10603 Then
                'bei einer bestimmten Variablen
                If msg.SenderID = 23491 Then
                    'ablegen des "Neuwertes"
                    tbReceiveMS.Text = msg.Data(0)
                End If
            End If
        Next
    End If
    'ablegen des aktuellen Zeitstempels
    tbTimeStamp.Text = IPS_Comm.TimeStamp
End Sub

Das war’s!

Für alle die keine lust haben selber zu tippen ist hier das Beispielprojekt zum runterladen

IPS_JSONRPC.zip (90.8 KB)

Das war’s dann.

Wenn es sonst noch Fragen, Anregungen oder Verbesserungen gibt, meldet Euch bitte.

Hallo,

da ja mit der Version 4.0 die SOAP Schnittstelle nicht mehr existiert, habe ich meine VB-Visualisierung auf die JSON Schnittstelle mit Nutzung dieser Anleitung hier umgestellt. In der Version 3.4 lief alles soweit gut. Ich konnte mit GetValue den Status der IPS-Variablen abfragen und mit SetValue die Variablen setzen. Nun habe ich IPS auf die Version 4.0 umgestellt. Dabei ergab sich, dass die Abfrage der Variablen wie vorher funktioniert. Wenn ich aber eine Variable setzen will, z.B. so:


 Private Sub Tuner_Click(sender As Object, e As EventArgs) Handles Tuner.Click
        CMD = Not IPS_Comm.IPS_Request("GetValue", "41776")
        IPS_Comm.IPS_Request("SetValue", "41776, " & Convert.ToString(CMD))
        Play(1)
    End Sub

erfolgt die Rückmeldung:

 "{"jsonrpc":"2.0","error":{"code":-32700,"message":"Parse error"},"id":null}"  

Irgendwie kommt JSON mit dem übersendeten String:


"{"jsonrpc":"2.0","method":"SetValue","params":[39742, True],"id":"null"}"

seit der Umstellung auf die Version 4.0 nicht zurecht.

Viele Grüße Frank

Fügst du denn korrekt den Content-Type als „application/json“ ein?

paresy

Oh Herr je - sorry, so tief stecke ich nicht drin, um Deine Frage zu verstehen (bin nur kleiner Freizeit IT’ler). :slight_smile:

Ich habe nur wie in der Anleitung beschrieben die Klasse in mein VB Projekt integriert und frage die Variablen ab bzw. setze die Variablen über IPS_Comm.IPS_Request („Befehl“, „Parameter“). Die Abfrage lautet als beim Setzen der Variable z.B. IPS_Comm.IPS_Request(„SetValue“,„64567, 10“).

In der Klasse wird dann die Sendung formuliert:



  Public Function IPS_Request(ByVal IPS_Method As String, ByVal IPS_Params As String) As Object

        'Deklaration
        Dim IPS_IP As String = "192.168.178.34"
        Dim IPS_User As String = "xxxxxx"
        Dim IPS_Password As String = "xxxxxxx"
        Dim SSL As Boolean = False
        Dim request As HttpWebRequest
        Dim response As HttpWebResponse
        Dim authInfo As String
        Dim IPS_URI As String
        Dim Send As String
        Dim Rec As String
        Dim jsDeserializer As New JavaScriptSerializer()

        'Call-Back für SSL
        ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf AcceptCert)

        'erstellen der IPS-URI -> wenn SSL dann https
        If SSL Then
            IPS_URI = "https://" & IPS_IP & ":82/api/"
        Else
            IPS_URI = "http://" & IPS_IP & ":82/api/"
        End If

        'erstellen der Authorisierung
        authInfo = IPS_User & ":" & IPS_Password
        authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))

        'erstellen des Request Headers
        request = HttpWebRequest.Create(IPS_URI)
        request.Headers("Authorization") = "Basic " & authInfo
        request.Method = "Post"

        'erstellen der JSON-RPC
        '{"jsonrpc":"2.0","method":"IPS_RunScript","params":[10693],"id":"null"}
        Send = "{" & Chr(34) & "jsonrpc" & Chr(34) & ":" & Chr(34) & "2.0" & Chr(34) & "," _
                   & Chr(34) & "method" & Chr(34) & ":" & Chr(34) & IPS_Method & Chr(34) & "," _
                   & Chr(34) & "params" & Chr(34) & ":[" & IPS_Params & "]," _
                   & Chr(34) & "id" & Chr(34) & ":" & Chr(34) & "null" & Chr(34) & "}"

        Try

            'Den request-Stream versenden
            Dim StreamWriter As StreamWriter = New StreamWriter(request.GetRequestStream)
            StreamWriter.Write(Send)
            StreamWriter.Close()

            'Den response-Stream empfangen
            response = request.GetResponse
            Dim StreamReader As StreamReader = New StreamReader(response.GetResponseStream)
            Rec = StreamReader.ReadToEnd
            StreamReader.Close()
            response.Close()

            Dim IPS_JSResponse As IPS_Request_Response = jsDeserializer.Deserialize(Of IPS_Request_Response)(Rec)
            IPS_Request = IPS_JSResponse.result

        Catch ex As Exception

            IPS_Request = "IPS Comm. Fault"

        End Try

    End Function


Der request Stream enthält den oben gezeigten String. Im response Stream steht dann beim Setzen der Variable der Fehler.

Gruß Frank

Hab den Fehler herausgefunden. In meiner Abfrage muss das Wort „True“ klein geschrieben werden also „true“.

Gruß Frank

Hallo alle zusammen,

ich habe das auch für Visual C# gelöst.
Besteht da Interesse?

Oh ja, sehr gerne! C# wäre cool! :slight_smile:

Viele Grüße
Peter

OK. Ich werde das mal zusammenschreiben und dann Online stellen.

Hallo alle zusammen,

ich werde in diesem Thread alles zusammentragen was benötigt wird für eine Kommunikation von Visual C# mit IP-Symcon über JSON-RPC.

Vorwort:

Ich werde mich beruflich mehr mit Visual C# beschäftigen müssen. Darum habe ich als erste Übung das Visual Basic Projekt „JSON-RPC über Visual Basic Step by Step” in Visual C# umgeschrieben und etwas optimiert. Da wir in unserer Firma die SPS-Programmierung vor zwei Jahren, mit der Umstellung auf TIA auf „strukturierten Text“ umgestellt haben, habe ich mich sehr schnell heimisch gefühlt.

Als erstes erstellen wir ein neues Projekt (Windows Forms-Anwendung) mit dem Namen „IPS_JSONRPC_C“.

Jason_RPC_C_5.png

Auf der Form haben wir folgende Elemente.

Der Code

Imports (benötigter Namespace):

Zu den bestehenden:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

kommen dies hinzu

using System.Net;
using System.IO;

Die Imports kommen noch vor der Klasse und dem Namespace.

Als nächstes öffnen wir mit einem Doppelklick auf den „btSenden“ Button die btSenden_Click Sub.

Die Variablen Deklaration:

Was wird benötigt?

Natürlich wollen wir einen „Request“ (Anfrage) verschicken und einen Response (Antwort) empfangen. Also schreiben wir als erstes:

WebRequest request;
WebResponse response;

Da die Autorisierung (Benutzername und Passwort) mit übertragen wird, brauchen wir noch:

string authInfo;

Des weiteren benötigen wir die IPS URI. Dafür benötigen wir:

string ipsUri;

Zu guter Letzt gibt es natürlich einen Send und Receive String:

string send, Rec;

Nun kommt der eigentliche Code.

Als erstes erstellen wir die IPS URI. Das sieht so aus:

ipsUri = „http://“ + tbIPS_IP.Text+ „:3777/api/“;

Als nächstes kommt die Autorisierung dran. Dazu schreiben wir:

authInfo = tbBenutzer.Text+ „:“ + tbPassword.Text;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));

Nun haben wir alle Informationen zusammengestellt für den Request-Header. Der sieht dann so aus:

request = WebRequest.Create(ipsUri);
request.Headers.Add(„Authorization“, "Basic " + authInfo);
request.Method = „POST“;

Als nächstes bauen wir uns den JSON-RPC Request zusammen. Wir erinnern uns an den Aufbau aus der Einleitung.

{„jsonrpc“:„2.0“,„method“:„IPS_RunScript“,„params“ :[10693],„id“:„null“}

Hinter „method“ kommt der eigentliche IPS-Befehl und nach „params“ kommt alles aus den runden Klammern des IPS-Befehls.

Wenn wir die JSON-RPC nachbauen sieht das so aus:

send = „{„jsonrpc“:„2.0“,„method“:“" + tbBefehl.Text+ „“,„params“:[" + tbParameter.Text+ „],„id“:„null“}“;

Nun habe wir alles zusammen um den Request-Stream zu versenden. Das sieht dann so aus:

StreamWriter StreamWriter = new StreamWriter(request.GetRequestStream());
StreamWriter.Write(send);
StreamWriter.Close();

Nun kommt der zweite Teil. Empfangen des Response (Antwort). Wir wollen natürlich den Response unseres Request haben. Also kommt in der ersten Zeile:

response = request.GetResponse();

Danach lesen wir den Stream ein und legen die Daten ab.

StreamReader StreamReader = new StreamReader(response.GetResponseStream());
Rec = StreamReader.ReadToEnd();

Am Ende dürfen wir nicht vergessen alles zu schliessen.

StreamReader.Close();
response.Close();

Die Empfangenen Daten können nun abgelegt werden.

tbReceive.Text = Rec;

Um die Antworten von IPS auszuwerten hat man zwei Möglichkeiten.

  1. Den Antwort-String händisch in seine Bestandteile zerlegen.
  2. Die Antwort von IPS in ein Objekt Deserialisieren.

Die elegantere Methode ist natürlich das Deserialisieren der Antwort durch C#. Diese Möglichkeit werden wir nutzen.

Als erstes brauchen wir einen Verweis auf „System.Web.Extensions“. Wir gehen in unserem Projekt mit einem Rechtsklick auf Verweis und dann auf Verweis hinzufügen. In dem sich öffnendem Fenster markieren wir „System.Web.Extensions“.

Jason_RPC_C_8.PNG

Der Code

Als erstes brauchen wir eine Hilfs-Klasse mit den selben Elementen die auch in der Antwort von IPS enthalten sind. Wir erinnern uns:

Eine JSON-RPC Antwort sieht bei IPS so aus:
{„result“:true,„id“:„null“,„jsonrpc“:„2.0“}

Bestandteile eines Response-Objekts

result -> Das Rückgabeobjekt der Funktion, falls kein RPC-Fehler auftrat.
id -> Enthält den gleichen Wert wie id des Requests. In unserem Fall fix „null“
jsonrpc -> Ein String mit dem Namen der JSON-RPC-Version. Momentan fix „2.0“

Also sieht unsere Hilfsklasse so aus:

public class IPS_Request_Response
{
public string result { get; set; }
public string id { get; set; }
public string jsonrpc { get; set; }
}

Nachdem wir unseren Verweis auf „System.Web.Extensions“ gemacht haben können wir diesen Import zufügen:

using System.Web.Script.Serialization;

Bei der Variablendeklaration benötigen wir noch:

JavaScriptSerializer jsDeserializer = new JavaScriptSerializer();

Das auswerten der Antwort sieht dann so aus:

IPS_Request_Response jsonData =
(IPS_Request_Response)jsDeserializer.Deserialize(Rec, typeof(IPS_Request_Response));

Nach dieser Zeile kann auf jedes Element der Antwort Zugeriffen werden. Das Ergebnis kann dann mit:

tbReceive.Text = jsonData.result;

abgefragt werden.

Diese Auswertung der Antwort gilt nicht für „IPS_GetSnapshot“ und „IPS_GetSnapshotChanges“.

Schauen wir uns eine Antwort von IPS nach einem „IPS_GetSnapshotChanges“ genauer an.

{„result“:[
{„Message“:10603,„SenderID“:39800,„TimeStamp“:3911 618,„Data“:["\u0005",false,"\u0005",1392919667]},
{„Message“:10603,„SenderID“:38994,„TimeStamp“:3911 619,„Data“:[false,false,false,1392919667]},
{„Message“:10804,„SenderID“:38874,„TimeStamp“:3911 620,„Data“:[false]},
{„Message“:10803,„SenderID“:38874,„TimeStamp“:3911 621,„Data“:[1369587830,0]},
{„Message“:11202,„SenderID“:35082,„TimeStamp“:3911 622,„Data“:[1,9,2,1392919667]}
],„id“:„null“,„jsonrpc“:„2.0“}

Es ist deutlich zu sehen das „result“ ein Array ist. Dieses Array bestehend aus:

Message ->MeldungsID. Siehe Liste der Konstanten im SDK in UIPSTypes.pas
SenderID ->ID von demjenigen der die Meldung verursacht hat
TimeStamp ->Zeitstempel
Data ->die aktuellen Daten

Ein Beispiel für Message aus der UIPSTypes.pas.

// — Message Constants
// — BASE MESSAGE
IPS_BASE = 10000; //Base Message

// — VARIABLE MANAGER
IPS_VARIABLEMESSAGE = IPS_BASE + 600; //Variable Manager Message
VM_CREATE = IPS_VARIABLEMESSAGE + 1; //Variable Created
VM_DELETE = IPS_VARIABLEMESSAGE + 2; //Variable Deleted
VM_UPDATE = IPS_VARIABLEMESSAGE + 3; //On Variable Update

Also steht in Message ein Wert von 10000 + 600 + 3 = 10603 bei einer Variablenaktualisierung.

Der Aufbau von „Data“ bei einer Variablenaktualisierung:

Data":[„Hallo“,false,„Hallo“,1392919667]

Der Aufbau von „Data“ bei einer Variablenänderung:

Data":[„Hallo Martin“,true,„Hallo“,1392919667]

Die Bestandteil sind:

[„Hallo Martin“, true, „Hallo“, 1392919667]

[Neuer Wert, Wertänderung (true/false), Alter Wert, Zeitstempel]

Bei einer Variablenänderung ist Data somit ein Array bestehend aus 4 Elementen (0-3)

Bei dieser Antwort habe ich zwei Hilfsklassen. Eine Hilfsklasse für „result“ und eine Hilfsklasse für die gesamte Antwort.

Als erstes die Hilfsklasse für „result“.

public class IPS_Result
{
public int Message { get; set; }
public int SenderID { get; set; }
public int TimeStamp { get; set; }
public object Data { get; set; }
}

Danach die Hilfsklasse für die Gesamte Antwort. Man beachte das result ein Array von der ersten Klasse „IPS_Result“ ist!!

public class IPS_SnapShot
{
public IPS_Result result { get; set; }
public string id { get; set; }
public string jsonrpc { get; set; }
}

Das auswerten der Antwort sieht dann so aus:

IPS_SnapShot jsonData = (IPS_SnapShot)jsDeserializer.Deserialize(Rec, typeof(IPS_SnapShot));

Nach dieser Zeile kann auf jedes Element der Antwort Zugegriffen werden.

Ich habe in dem Beispielprojekt bereits alles in eine Klasse gepackt.

Wenn es noch Fragen gibt meldet Euch bitte!!

IPS_JSONRPC_C.zip (65.7 KB)