TwinCAT3 - Variablen als JSON per MQTT an MessageBroker übertragen Problem: REAL Variable verändert Wert

Perold

Level-2
Beiträge
40
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

ich grübel momentan über folgendem Problem.

Ich möchte gerne Variablen aus einem Beckhoff IPC CX5120 per MQTT an einen MQTT MessageBroker publishen. Diese Variablen subscribe ich dann später mit NodeRED wieder. Das funktioniert auch sehr gut. Dazu habe ich mich an das Beispiel von Beckhoff gehalten.

Auf folgendes Problem bin ich aber gestoßen:
Ich möchte ein Set aus 3 Variablen als JSON übertragen und muss dafür in TwinCAT eine Struktur anlegen.
Code:
TYPE ST_Teststruktur_MQTT :
    STRUCT
        Test1 : REAL;
        Test2 : REAL;
        Test3 : DINT;
    END_STRUCT
END_TYPE

Aus diesem Struct mache ich ein JSON und übergebe das dann dem FunctionBlock FB_IotMqttClient.
Der Programm Code sieht so aus:
Code:
IF bSetParameter THEN
    bSetParameter               := FALSE;
    fbMqttClient.sHostName      := '172.17.10.111';
    fbMqttClient.nHostPort      := 1883;
//  fbMqttClient.sClientId      := 'MyTcMqttClient';
    fbMqttClient.sTopicPrefix   := '';
//  fbMqttClient.nKeepAlive     := 60;
//  fbMqttClient.sUserName      := ;
//  fbMqttClient.sUserPassword  := ;
//  fbMqttClient.stWill         := ;
//  fbMqttClient.stTLS          := ;
    fbMqttClient.ipMessageQueue := fbMessageQueue;
END_IF

fbMqttClient.Execute(bConnect);



IF fbMqttClient.bConnected THEN
    fbTimer(IN:=TRUE);
    IF fbTimer.Q THEN // publish new payload every second
        fbTimer(IN:=FALSE);
 
    
        // Fill data
        ST_Teststruktur_MQTT.Test1 := 10.5;            //fix eingetragene Werte zum testen
        ST_Teststruktur_MQTT.Test2 := 10.4;            //fix eingetragene Werte zum testen
        ST_Teststruktur_MQTT.Test3 := 33;            //fix eingetragene Werte zum testen
 
        //Convert to JSON string
        fbJsonWriter.ResetDocument();
        fbJsonDataType.AddJsonValueFromSymbol(fbJsonWriter, 'ST_Teststruktur_MQTT', SIZEOF(ST_Teststruktur_MQTT), ADR(ST_Teststruktur_MQTT));
        MessageToPublishJson := fbJsonWriter.GetDocument();
 
        //Publish
        fbMqttClient.Publish(sTopic:= TopicToPublishJson,
        pPayload:= ADR(MessageToPublishJson),
        nPayloadSize:= LEN2(ADR(MessageToPublishJson)),            //+1 weg? im Beispiel von Beckhoff ist eine +1 dahinter
        eQoS:= TcIotMqttQos.AtMostOnceDelivery,
        bRetain:= FALSE,
        bQueue:= FALSE);
     END_IF
END_IF

Das ganze funktioniert, allerdings verändert sich der Wert der Variable ST_Teststruktur_MQTT.Test2 von 10.4 auf 10.399999618530274. Komischerweise gibt es diesen Fehler bei der anderen REAL Variable mit dem Wert 10.5 nicht.
Diese Ungenauigkeit passiert schon in TwinCAT3 selbst, siehe Screenshot. Nun die Preisfrage, wo liegt der Fehler?


Gruß
Perold



spsforum.jpg
 
Das ganze funktioniert, allerdings verändert sich der Wert der Variable ST_Teststruktur_MQTT.Test2 von 10.4 auf 10.399999618530274.
Das sehe ich auch schon in TwinCAT3 selbst. Nun die Preisfrage, wo liegt der Fehler?
Nirgends, denn das ist kein Fehler, sondern völlig "normal". Der Grund liegt in der Art, wie Fließkommazahlen abgelegt werden. Nicht jede Fließkommazahl kann genau abgelegt werden und 10,4 gehört dazu.
 
Okay, wieder ein Stück schlauer geworden. Dann runde ich auf der Empfängerseite einfach wieder und alles ist super.

Vielen Dank für die Aufklärung!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Dann runde ich auf der Empfängerseite einfach wieder und alles ist super.
Dann verfälschst Du die empfangenen Werte - das würde ich nicht als "alles ist super" bezeichnen.
Das Runden kannst Du meistens der Anzeige überlassen.

1) Selber runden bringt nichts: wenn Du 10.3999996185302734375 irgendwie auf 10.4 rundest und dann in eine REAL-Variable speicherst, dann steht in der REAL-Variable wieder 10.3999996185302734375 drin, weil 10.4 nicht genau als IEEE-754-Fließkommazahl codiert werden kann.

2) 10.4 kann gar nicht als REAL-Zahl übertragen werden, der Sender kann also gar nicht 10.4 gesendet haben. Du willst aber willkürlich eine empfangene Zahl auf irgendeine "schöne" Zahl runden, die es so im Original gar nicht gab. Wozu Daten verfälschen? Wenn der Wert ursprünglich 10.4 war (vielleicht als Text/String?) und im Empfänger soll/muß 10.4 ankommen, dann musst Du den Wert als Ganzzahl (siehe Beitrag #5) oder als Text/String übertragen.

Harald
 
Dann runde ich auf der Empfängerseite einfach wieder und alles ist super.
Wenn die Zahl auf der Empfängerseite wieder REAL (oder LREAL) ist, wozu "runden"?
Dann wird wieder (genauso wie auf der Senderseite) zur nächstbesten darstellbaren Zahl gewandelt und nichts hat sich durch das zusätzliche Runden geändert.
Der Begriff "NachkommaStellen" ist nur für die Darstellung/Anzeige von Bedeutung.
Falls man die Möglichkeit hat, die Darstellung zu formatieren, so kann man das tun, z.B. wenn man REAL in einen String wandelt oder die BildschirmAnzeige oder die DruckAusgabe beeinflussen will, ABER an dem Inhalt der REAL-Variablen ändert das nichts.

Harald war mal wieder schneller! ;)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich bevorzuge im Normalfall eigentlich immer die Variante, dass ich mir die Anzahl nötiger Kommastellen festlege und die Gleitkommazahl dann in eine Ganzzahl umwandle, indem ich mit der entsprechenden Zehnerpotenz multipliziere. Denn über die geforderte Genauigkeit muss man sich sowieso Gedanken machen. Ganzzahlen haben grosse Vorteile: meistens weniger Speicherbedarf, schneller in der Ausführung, nie Zahlen, die nicht dargestellt werden können (wie hier). Bei grossen Zahlen kann es auch nicht passieren, dass sich plötzlich durch Addition von kleinen Zahlen die Ausgangs-Zahl nicht mehr ändert, weil die Anzahl signifikanter Stellen begrenzt ist.
Meistens schaue ich am Anfang darauf, dass gar keine Gleitkommazahlen entstehen. Denn auch von den Sensoren kommen die Werte normalerweise als Ganzzahlen.
 
Zurück
Oben