Castingproblem

Einrichtung der openHAB Umgebung und allgemeine Konfigurationsthemen.

Moderatoren: seppy, udo1toni

Antworten
kaloschke
Beiträge: 193
Registriert: 29. Jan 2019 07:20
Answers: 0

Castingproblem

Beitrag von kaloschke »

Hallo,
ich habe ein Item

Code: Alles auswählen

Number IntensitySouth                   "Helligkeit Süden"
und eine Regel mit der Zeile

Code: Alles auswählen

 val dimVal = (IntensitySouth.state as Number)
Dies führt beim Start zu einem Fehler mit der Meldung

Code: Alles auswählen

Script execution of rule with UID 'garden-23' failed: Could not cast NULL to java.lang.Number;
Eine Initialisierung in der Regel für den Systemstart mit

Code: Alles auswählen

IntensitySouth = 0
scheitert ebenfalls mit dem Fehler

Code: Alles auswählen

Script execution of rule with UID 'home-1' failed: An error occurred during the script execution: Cannot assign a value in null context.
Habe es auch mit einem Timer in der Startroutine versucht. Dann gibts für die Startroutine keinen Fehler, aber das oben beschriebene Problem bleibt bestehen.

Und auch sowas

Code: Alles auswählen

 if ((IntensitySouth.state as Number) != null) {
ist dann natürlich auch nicht möglich.

Gibt es hier eine Lösung?

Viele Grüße

kaloschke
Beiträge: 193
Registriert: 29. Jan 2019 07:20
Answers: 0

Re: Castingproblem

Beitrag von kaloschke »

Noch ein paar Ergänzungen.
Ich habe eine Datei home.rules mit einer Regel, die beim Systemstart ausgeführt wird. Dort wird in einer Verzögerung mittels Timer das Item IntensitySouth gesetzt.
Die Regel, in der das Item benutzt wird, befindet sich aber in einer Datei garden.rules. Diese wird dann wohl "analysiert", bevor der obige Timer beendet wurde, schätze ich mal.
Kann mal die Abarbeitungsreihenfolge beeinflussen?

kaloschke
Beiträge: 193
Registriert: 29. Jan 2019 07:20
Answers: 0

Re: Castingproblem

Beitrag von kaloschke »

Problem ist wohl gelöst.
Die zweite Datei garden.rules ist "historisch" gewachsen, um die home.rules zu verschlanken. Ich habe nun dort ebenfalls eine Regel für den Systemstart und initialisiere dort - verzögert - die Items.

Benutzeravatar
udo1toni
Beiträge: 15244
Registriert: 11. Apr 2018 18:05
Answers: 242
Wohnort: Darmstadt

Re: Castingproblem

Beitrag von udo1toni »

Da geraten ein paar Dinge durcheinander...
kaloschke hat geschrieben: 22. Okt 2023 20:40

Code: Alles auswählen

 val dimVal = (IntensitySouth.state as Number)
Dies führt beim Start zu einem Fehler mit der Meldung
Wenn Du in einer Rule den Status eines Number Items als Zahl verwenden willst, ist das Casting die saubere Art, das zu tun, und oft genug ist es auch die einzig funktionierende Variante.
Da Du aber nur numerische Status nach Number casten kannst, ist es sinnvoll, zunächst zu prüfen, ob der Status vom Typ Number ist:

Code: Alles auswählen

    var dimval = 0.0
    if(IntensitySouth.state instanceof Number)
        dimVal = IntensitySouth.state as Number
Hier mit einer lokalen Variablen, die zunächst einen Default Wert zugewiesen bekommt. Nur wenn der Item Status numerisch ist, wird dieser auch zugewiesen.
Noch eleganter ist die Variante mit dem ternären Operator:

Code: Alles auswählen

    val dimval = if(IntensitySouth.state instanceof Number) (IntensitySouth.state as Number) else 0
Hier kann man auch eine lokale Konstante verwenden, weil zum Zeitpunkt der Zuweisung entschieden wird, ob nun der Default Wert oder das Item Status zugewiesen wird. Aber immer aufpassen, im ersten Fall ist es wichtig, 0.0 als Wert zu nutzen, damit, falls der Status kein Integer Wert ist, die Anschließende Zuweisung keinen Fehler auslöst.
Natürlich kann man auch dafür sorgen, dass der Wert sicher als Integer Wert behandelt wird - das ist oftmals überhaupt der Grund für das Type Casting, man möchte den Status als Objekt mit bestimmten Methoden verwenden, z.B. .intValue, .floatValue oder gar .doubleValue, weil eine nachfolgende Berechnung zwingend einen bestimmten Datentyp erwartet.
kaloschke hat geschrieben: 22. Okt 2023 20:40 Eine Initialisierung in der Regel für den Systemstart mit

Code: Alles auswählen

IntensitySouth = 0
scheitert ebenfalls
Du kannst einem Item nicht einfach einen Wert mittels = zuweisen, ein Item ist ein Item, keine Variable.
Stattdessen musst du die Methode .postUpdate() verwenden:

Code: Alles auswählen

IntensitySouth.postUpdate(0)
Allerdings ist openHAB kein Echtzeit System, mehr noch, es arbeitet extrem multithreaded, je nach Konfiguration laufen auf einem openHAB System 60 bis 260 Threads, die alle mehr oder weniger beschäftigt sind (vielleicht ist die Obergrenze sogar noch höher, aber die Untergrenze dürfte ungefähr bei 60 liegen). openHAB kann 0,1 ms für das postUpdate brauchen oder auch 10 s, das wäre dennoch kein Fehler, sondern allenfalls eine überdurchschnittlich lange Wartezeit :)
Warum ist das wichtig? Du solltest keinesfalls darauf vertrauen, dass ein Item "ganz bestimmt" einen gültigen Wert hat, selbst wenn Du über eine Regel zum Systemstart einen Wert in das Item schreibst, bei den passenden Umständen knallt es dann trotzdem. Besser immer auf instanceof ... testen, damit bist Du auf der sicheren Seite.
kaloschke hat geschrieben: 22. Okt 2023 20:40 Und auch sowas

Code: Alles auswählen

if ((IntensitySouth.state as Number) != null) {
ist dann natürlich auch nicht möglich.
Genau. das Problem ist ja, dass das Casting selbst den Fehler auslöst. Bevor also die eigentliche Prüfung stattfinden kann, ist das Malheur schon passiert. Deshalb gibt es instanceof :)
Es gibt aber bei dieser Prüfung noch einen Fehler :) oder zumindest ist es unsauber.
Wenn Du auf ungleich (oder gleich) null prüfst, wird openhAB immer mosern, weil es unsauber ist. Stattdessen musst Du auf identisch bzw. nicht identisch prüfen:

Code: Alles auswählen

var meineVariable = null
if(meineVariable === null)
    // wird ausgeführt, da die Bedingung erfüllt ist
if(meineVariable !== null)
    // wird nicht ausgeführt, da die Bedingung nicht erfüllt ist
if(meineVariable == null)
    // führt zu einer Warnung, weil nicht auf identisch geprüft wurde
    
Der feine Unterschied: bei === wird geprüft, ob der Zeiger der Variablen auf die gleiche Speicherzelle zeigt, in der der null-Wert gespeichert ist. Das hat also mit der internen Verwaltung des Systems zu tun. Hintergrund ist, dass null ja kein Wert ist, entsprechend kann man auch nicht auf Gleichheit prüfen. Stattdessen werden alle Variablen, welche bei der Initialisierung keinen Wert zugewiesen bekommen, mit einem Zeiger auf die selbe Speicherzelle initialisiert, welche das System dann als null wertet.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet

kaloschke
Beiträge: 193
Registriert: 29. Jan 2019 07:20
Answers: 0

Re: Castingproblem

Beitrag von kaloschke »

Oh. Du hast noch geantwortet. Habe ich gar nicht mitbekommen :-(. Und dann wieder so ausführlich.
Die Zuweisung des Wertes hatte ich mittlerweile so wie du schreibst schon mit

Code: Alles auswählen

IntensitySouth.sendCommand(50)

beim Startvorgang von OH vorgenommen.
Und die Sache mit dem Vergleich auf Null

Code: Alles auswählen

  if ((IntensitySouth.state as Number) !== null) ...
hatte ich mittlerweile selber in Netz gefunden.
Wäre hier aber schneller gegangen :D
Jedenfalls klappts jetzt an dieser Stelle.
Vielen Dank, wenn auch spät

Antworten