Step 7 JSON-String in S7 1200 erstellen?

Zuviel Werbung?
-> Hier kostenlos registrieren
Ich glaube das ist eben auf der SPS die Schwierigkeit, dass du keine Attribute einer Variable mit geben kannst (ich kenne mich in der Hochsprache nicht aus was möglich ist und was nicht).

Deswegen musst du quasi für jede einzelne Zeile (Element) vorgeben, wie diese formatiert ist, was die Funktion zum Serialisieren zu erwarten hat.

Bekomme ich einen JSON String, dann kann ich mir den wieder genauso in die einzelnen Elemente zerlegen und dann weiterverarbeiten. Das geht und hab ich auch schon getestet.

JSON ist in dem Sinne in Verbindung mit MQTT eine Möglichkeit, wirklich unabhängig von verschiedenen Clients, Systemen, Sprachen usw Daten zentral auszutauschen, ohne dass die Teilnehmer in direkter Kommunikation stehen müssen. Da maschinenlesbar und universell

Mitarbeiter im Büro können genauso gut mit den Daten arbeiten und diese verarbeiten, wie andere Maschinen oder Systeme, klatscht man dazwischen dann noch eine Datenbank, so kann man quasi mit der Datenbank dann auch noch auf den Broker "hören" und sich die Daten dort zentralisiert abgreifen.

Anderes Beispiel dazu, möchte zB jemand aus der Produktion einen digitalen Zwilling der Halle erstellen, so kann er quasi über MQTT abgreifen, wo welche Anlage steht, wo welcher Sensor verbaut ist, was die Produktnummer und die Eigenschaften des Sensors sind, dazu braucht er dann keinen veralteten Eplan mehr, sondern kann diese Daten direkt über den Broker abgreifen und so auch Hardware Änderungen direkt mitbekommen
 
@DCDCDC :
Den JSON selbst brauchst du mir nicht zu erklären - den kenne ich aus der .Net-Welt (VB- / C#-Programmierung) zur Genüge. Ich kenne also auch dessen Vorzüge und für die Welt war er aus meiner Sicht auch mal gemacht.
Das es in der SPS-Welt auch funktioniert, wenn auch mit hohem Aufwand, fand ich interessant.
Ich hatte jetzt halt nur überlegt, ob es nicht auch einen komplexeren als den von dir verwedeten Serialisierer gibt, den man gleich das komplette Objekt übergeben kann. Das wäre dann sehr viel Speicher-schonender und auch gleich viel universeller anwendbar. Das war im Grund ja auch die Frasge ...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das weiß ich tatsächlich nicht, ich verwende da auch nur die Siemens Bibliothek.. wäre natürlich schön wenn es sowas geben würde, das wäre dann aber ein Thema für den Eigenbau.

Allein die Funktion für Mqtt ist bei Siemens riesig, irgendwie 3000 Zeilen, der Serialisierer hat auch einige hunderte.
 
Also soweit ich das verstanden habe, müsste man in dem "manuellen-string" Ansatz, den ich beschrieben hatte, den auch manuell zerpflücken. Bei dem LStream Ansatz, würde man über die Objekte iterrieren und nach den informationen suchen ... das wäre sicher der einfachere Ansatz. Allerdings bezweifle ich stark, dass es etwas gibt mit dem man, wie von dir vorgeschlagen, Datenblöcke quasi "exportieren" kann. Da sind wir in der SPS welt einfach viel zu weit von der Hochsprachen Welt entfernt. (Allerdings würde ich mich sehr freuen, wenn ich da falsch liegen würde)
 
Ich will mir auf jeden fall mal die Experimente mit SparkplugB ansehen ... hier werden nämlich die Payloads nicht als JSON strings übertragen, was ich nach XML für so ziemlich die ineffizienteste variante halte (was die Größe der Nachricht auf der Leitung angeht) ... bei SparkplugB werden die Daten im binar-format via Protobuf encodiert/decodiert und übertragen.

Das, was ich mit JSON gemacht habe mit LStream zu machen ist Punkt 1 auf meiner Todo-Liste für die nahe Zukunft und dann mit SparkplugB zu experimentieren ist dann definitiv Punkt 2. Letzteres ist auch definitiv, was ich mir so als Zukunfts-Vision für das ganze sehe.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe jetzt die Möglichkeit mit den LStream Bausteinen getestet und muss sagen, dass das so auf einer kleinen PLC (S7 1200) absolut nicht nutzbar ist. Außer man hat vor nur extrem wenige Daten zu übertragen. Damit zu arbeiten ist sicherlich sehr komfortabel und einfach, belegt aber dermaßen viel Arbeitsspeicher, dass das für mich nicht in Frage kommt.

Mit dem Zusammenbau eigener Strings habe ich dann auch noch ein wenig experimentiert und muss sagen, dass das wesentlich effizienter funktioniert. Ich mache das in etwa so:

C-ähnlich:
// Erst baue ich in einem Array of Strings[150] (nur 150 Zeichen lang, um Arbeitsspeicher zu sparen), jeweils ein Key:Value Paar zusammen
// Ein Key:Value Paar darf also insgesamt nicht länger als 150 Zeichen sein
Array[1] := CONCAT(IN1 := '"Key_von_Integer":', IN2 := ConvertIntToString(IN := "datenbaustein".meineIntVariable));
Array[2] := CONCAT(IN1 := '"Key_von_String":"', IN2 := "datenbaustein".meineStringVariable, IN3 := '"'); //hier muss nach dem : und am Ende ein "
Array[3] := CONCAT(IN1 := '"Key_von_Real":', IN2:= ConvertRealToString(IN := "datenbaustein".meineRealVariable));
// Die Convert Funktionen habe ich mir vorher in FCs für jeden Datentyp gebaut

// Dann lasse ich eine For Schleife über die Daten laufen
// (Die Schleife muss einen Wert höher sein, als das Array, damit man die Klammer zum Schließen des Objekts anhängen kann)
For #counter := 0 TO 4 DO
    IF #counter = 0 THEN
        StringToChars(IN := '{',
                     PCHARS := #POSVariable,
                     CNT := #CountVariable,
                     OUT := mqttByteArray);
    ELSIF #counter = 4 THEN
        StringToChars(IN := '}',
                     PCHARS := #POSVariable,
                     CNT := #CountVariable,
                     OUT := mqttByteArray);
    ELSE
        StringToByte(IN := Array[#counter],
                     PCHARS := #POSVariable,
                     CNT := #CountVariable,
                     OUT := mqttByteArray);
    END_IF;
    #counter := #counter + 1;
    #POSVariable := #POSVariable + #CountVariable;
END_FOR;

(Das ist jetzt aber nicht aus dem Programm kopiert, deshalb kann es sein, dass der Code so nicht in der SPS funktioniert. Ich habe jetzt nur versucht das so gut, wie möglich aus meinem Gedächtnis abzurufen. Ich glaube die Konvertierung von String zu ByteArray ist etwas anders.)

Nach dem Zusammenbau der Daten, kann dann noch ein Topic gesetzt werden und dann gesendet werden.
So eine Funktion habe ich für jeden Datenbaustein, den ich übertragen will, in einem FC geschrieben, damit dieser dann mit einem Triggerbit aufgerufen werden kann, damit die Daten nur bei einem Event konvertiert und übertragen werden.

Diese Variante belegt extrem viel weniger Arbeitsspeicher, als die Variante mit der LStream Bibliothek und funktioniert bisher ziemlich gut.
Mein nächstes Ziel ist, das Zusammensetzen der Strings zu ersetzen, um dann nur noch jeweils den Key als String zu haben und den Wert aus dem DB gleich in Byte umzuwandeln. Das sollte dann eventuell nochmal etwas mehr Arbeitsspeicher frei machen. (Hoffe ich zumindest)

Vermutlich ist meine Lösung etwas unkonventionell, funktioniert aber, wie schon erwähnt, ziemlich gut.
Vielleicht hilft das ja Einigen, ihre eigenen Lösungen zu entwickeln und das Ganze vielleicht noch zu verbessern.
 
Zuletzt bearbeitet:
Kein schlechter Vorschlag ...
Noch effizienter wäre es warscheinlich wenn man sich einen DB erstellt, der JSON komplett enthält und hinter den entsprechenden Keys Platzhalter hat. Nun braucht man nur noch die Platzhalter durch die Variablen/Werte zu ersetzen, die man übertragen will und am Ende das Ganze in ein passend großes Byte-Array (ggf. einen anderen DB) serialisieren welches dann übertragen wird.
 
Kein schlechter Vorschlag ...
Noch effizienter wäre es warscheinlich wenn man sich einen DB erstellt, der JSON komplett enthält und hinter den entsprechenden Keys Platzhalter hat. Nun braucht man nur noch die Platzhalter durch die Variablen/Werte zu ersetzen, die man übertragen will und am Ende das Ganze in ein passend großes Byte-Array (ggf. einen anderen DB) serialisieren welches dann übertragen wird.
Das ist keine schlechte Idee, aber in der SPS Welt so nicht machbar, bzw. ineffizient, weil Strings immer ziemlich viel Speicher einnehmen.

Ich habe jetzt einen anderen Ansatz, den ich kommende Woche testen werde und falls er funktioniert und noch mehr Arbeitsspeicher spart, werde ich ihn hier teilen. (Der Ansatz ist wie in meinem letzten Beitrag erwähnt mit weniger Strings und direkter Umwandlung der Werte)

Das Problem in einer SPS ist, dass ich nichtmal einen String, wie in einer Hochsprache zusammensetzen kann, z.B.: '{"+key+":"+var+"}' funktioniert nicht. Das müsste man alles mit entsprechenden Concat Funktionen zusammensetzen, was aber wie gesagt viel Arbeitsspeicher frisst.

Mein erster Ansatz war tatsächlich auch, erst einen komplett JSON String zusammenzusetzen und diesen dann zu serialisieren. Das hat aber schon nicht funktioniert, weil mein JSON String zu lang für eine Variable vom Typ String war. Strings sind in einer SPS auf 254 Zeichen begrenzt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Naja ... um die Zeichenketten der Key-Namen kommst du nun mal nicht herum. Du hast meinen Vorschlag leider nicht richtig verstanden.
Ich wollte die Keys, die ja feststehen, in einem DB quasi als Konstanten ablegen.
Ich würde zu keinem Zeitpunkt den kompletten JSON als String generieren sondern als fertiges Byte-Array (bestehend aus den Keys aus dem DB und den gewandelten Variablen dazu), welches dann übertragen wird.
 
Naja ... um die Zeichenketten der Key-Namen kommst du nun mal nicht herum. Du hast meinen Vorschlag leider nicht richtig verstanden.
Ich wollte die Keys, die ja feststehen, in einem DB quasi als Konstanten ablegen.
Ich würde zu keinem Zeitpunkt den kompletten JSON als String generieren sondern als fertiges Byte-Array (bestehend aus den Keys aus dem DB und den gewandelten Variablen dazu), welches dann übertragen wird.
Okay dann habe ich Deinen ersten Satz auf jeden Fall falsch verstanden: 😬
Noch effizienter wäre es warscheinlich wenn man sich einen DB erstellt, der JSON komplett enthält und hinter den entsprechenden Keys Platzhalter hat.
Aber ist ja nicht schlimm, jetzt weiß ich ja, wie Du das meinst. :)

Die Keys müssen Strings sein und stehen auch fest. Da stimme ich Dir voll und ganz zu. Diese würde ich tatsächlich auch in einem Array of Strings ablegen, um dann mit einer For Schleife über alle Keys iterieren zu können. Vermutlich kann man da noch einiges optimieren, deswegen bleibe ich da auf jeden fall auch dran.

Wenn mein nächster Ansatz gut funktioniert, leicht mit Daten zu versorgen ist und noch mehr Arbeitsspeicher spart, dann bin ich glaube ich auch zufrieden.
Das Zusammensetzen der Strings aus meinem Beispiel funktioniert auf jeden Fall auch schon ziemlich gut. Gegebenenfalls würde ich vielleicht sogar dabei bleiben, wenn der neue Ansatz keinen Mehrwert bietet.
 
@DavidP :
Ich hatte dir schon geschrieben, dass ich deinen Ansatz interessant finde - ist eigentlich ungerecht ... der ist schon klasse. Wenn du das noch etwas aufbereiten könntest wäre das sogar etwas für dem FAQ-Bereich hier im Forum. Es kommt einem JSON-Serialisierer jedenfalls schon sehr nahe (mal egal woher man die Key-Namen bekommt).
Vielleicht könntest du deine StringToByte-Funktion auch noch posten ...
Ich selber brauche das nicht ... aber hier im Forum ist gerade dieses Thema schon in letzter Zeit häufiger aufgekommen.
Naja ... und wenn ich das mit dem Siemens-Kram ( :rolleyes: ) vergleiche ...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@Larry Laffer:
Vielen Dank! :)
Ich kann das sehr gern noch ein bisschen aufbereiten und auch nochmal den richtigen, funktionierenden Code hier posten.

Die StringToByte Funktion ist tatsächlich eigentlich die STRING_TO_CHARS Funktion von Siemens selbst (Das war mir nur, als ich hier ins Forum gepostet habe, entfallen). Die funktioniert dafür ziemlich gut und wird in dem Beispiel von Siincos: SIMATIC S7-1200/1500 als MQTT Publisher (Teil 1) auch verwendet, um einen JSON String zu einem ByteArray umzuwandeln.

An meinem neuen Ansatz (gleich umwandeln, ohne vorher einen String zusammen zu setzen) bin ich auch schon dran, der funktioniert allerdings noch nicht. Da werde ich dann auch weiter von berichten.
 
Naja ... und wenn ich das mit dem Siemens-Kram ( :rolleyes: ) vergleiche ...
Na ja, Siemens hat schon früher - sagen wir mal "merkwürdige" - Software *) produziert und sich hier wahrscheinlich an der Realisierung einer eierlegenden WollMilchSau (verdammt, jetzt ist mir doch tatsächlich Marios neue Formulierung dafür schon wieder entfallen! :mad: ) versucht.
Das kann sehr schnell in etwas speicherplatzverschwendendes ausarten.

*) Als Beispiel fallen mir dazu spontan die Siemens-GrayCodeInDual-und-DualInGrayCodeUmwandelFbs aus S5-Zeiten ein. Die waren endlos lang und konnten zum Ausgleich dafür nur maximal 15-Bit-Zahlen korrekt wandeln.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich würde hier gerne mal eine Idee einbringen, die evtl. einige Probleme eliminieren könnte.
Mein eigentliches Ziel ist es nicht JSON daten via MQTT zu verschicken, sondern daten im Protobuf Format (ein binär-format) um die Daten zu übertragen. Da kann ich die Struktur vorgegeben und spare mir die übertragung der Keys. Im Idealfall würde ich gerne das SparkplugB format nutzen, allerdings ist das ein nicht gerade kleines unterfangen.
 
Ich würde hier gerne mal eine Idee einbringen, die evtl. einige Probleme eliminieren könnte.
Mein eigentliches Ziel ist es nicht JSON daten via MQTT zu verschicken, sondern daten im Protobuf Format (ein binär-format) um die Daten zu übertragen. Da kann ich die Struktur vorgegeben und spare mir die übertragung der Keys. Im Idealfall würde ich gerne das SparkplugB format nutzen, allerdings ist das ein nicht gerade kleines unterfangen.
Das kann man natürlich auch machen, das geht aber auch direkt schon mit der Serialize Funktion von Siemens, wenn man vor hat die Daten einfach nur als Bytestream, ohne Keys, zu versenden.

Interessieren würde mich das allerdings trotzdem. Vor allem das Format, in dem die Daten übertragen werden sollen. Die Frage ist, ob sich der Aufwand lohnt, wenn du sagst, dass das Ganze kein kleines Unterfangen sein dürfte.

Das Ziel hier ist aber auf jeden Fall trotzdem, dass man die Daten aus der SPS schon in ein gut lesbares Format (in dem Fall JSON) bringt und dann versendet.
Mein anderer Ansatz funktioniert mittlerweile, dazu würde ich nochmal einen etwas ausführlicheren Beitrag schreiben, aber jetzt nur grob, wie ich vorgegangen bin, bzw. was noch gemacht werden muss, um einen DB in das JSON Format zu bekommen:

Ich habe eine Funktion gebaut, die das Konvertieren der Daten übernimmt und an den Baustein müssen nur noch ein paar Infos eingetragen werden, die man aber größtenteils aus den DBs kopieren kann:
1. DBNummer als Int
2. KeyArray(*) of String[50] (keys dürfen maximal 50 Zeichen lang sein)
3. TypeArray(*) of String[6] (Datentypen als String, es sind nur bestimmte Datentypen zugelassen, bzw. kann man z.B. mehrere Chars zu einem String kombinieren)
4. OffsetArray(*) of Real (Die Offsetwerte aus dem DB)
5. Done Bit, um die Daten zu versenden, wenn die Konvertierung abgeschlossen ist.
(Im FC läuft eine For Schleife, die pro Zyklus der Sps, nur 50 Werte bearbeitet, um die Zykluszeit nicht negativ zu beeinflussen. Wenn ich z.B. einen DB mit 1500Byte Größe in einer einzigen For Schleifen, ohne zu splitten, abarbeite, geht die Zykluszeit schon auch mal auf 250-300ms, was einen Fehler in der CPU auslöst. Daher die gesplittete For Schleife)
6. JSONByteArray

Ich kann also relativ schnell eine große Menge an Daten übertragen und spare sowohl Arbeitsspeicher, als auch CPU Leistung. Da die Daten im Konvertierungsbaustein temporäre Variablen sind, werden alle Daten immer wieder überschrieben. Das Einzige, was in dem fall den Arbeitsspeicher belastet, ist das ByteArray, in dem sich die JSON Daten als Bytestream befinden.

Vielleicht noch als Info: Das Ganze geht natürlich leider nur mit nicht optimierten Datenbausteinen.

Ich werde das Ganze nochmal etwas aufbereiten, mit detaillierteren Infos zum Vorgehen und dann hier posten.
 
Naja ... ich denke wenn ich einfach für meine use-cases da was zimmere, ist das vermutlich gemessen am Aufwand zu viel arbeit.
Jedoch setzt sich in den letzten Jahren immer mehr MQTT durch und hierauf das SparkplugB ... man hätte dann am ende ein gemeinsames format, und könnte automatisch mit SparkplugB enabled Geräten kommunizieren. Dieser standard hätte in meiner Ansicht doch erhebliche vorteile.
 
Zurück
Oben