[Semaphore] Skript nur ausführen...

Hallo Franz,

es mag sein, dass Dein Skript tatsächlich so schnell durchläuft, da es im Wesentlichen nur aus der Zuweisung eines Text-Strings an eine Externe Variable besteht.

Grundsätzlich kannst Du so vorgehen:

  1. feststellen, wie groß die Zeitspanne zwischen dem ersten und dem letzten Trigger ist (im Log File nachschauen)
  2. zur Sicherheit den errechneten Wert verdoppeln
  3. diesen Wert im Befehl IPS_SemaphoreEnter(…) verwenden (mindestens aber 1ms)
  4. feststellen, wie lange das Skript läuft (im Log File nachschauen)
  5. falls die Laufzeit kürzer ist, als die oben errechnete Zeitspanne, dann die fehlende Laufzeit durch IPS_Sleep(…) auffüllen (hier mindestens 5ms angeben, da die Auflösung der internen Timer in Windows oft nicht genau genug ist)

Gruß
HJH

Danke HJH,

so hab ich mir das auch gedacht. Ich kuck das mal alles nach. Mit einem IPS_Sleep(100) war ich mal richtig. Nur wird die Zeit wohl zu gross sein.

mfG Franz

So, also nach ein paar Tagen, wenn ich denke IPS_SemaPhore verstanden zu habe, krieg ich dann wieder einen Dämpfer. Es klappt einfach nicht, sogar wenn ich den Wert auf 2000 setzte (nur testweise)
Das Skript wird immer weiterhin 3 mal durchlaufen, auch bei allen anderen Skripten die auf ‚OnUpdate‘ angewiesen sind.

Das ist ein echtes problem, und ich wäre echt froh, wenn jemand hilfen könnte

Edit:

Ok, ich habe es anders gelöst. Ich habe IPS_GetUpdateTime benutzt und das klappt super.

Franz

Hallo Franz,

ich habe mir extra für diesen Zweck ein Test-Skript geschrieben, in dem ich dieses Verfahren anwende.

Das Ergebnis:
Es funktioniert einwandfrei!

Am Anfang lief es jedoch noch nicht. Es stellte sich dann heraus, dass ich den Befehl falsch geschrieben hatte.
Ich hatte bei Semaphore versehentlich das e weggelassen. Die Fehlermeldung findet man natürlich nur im Debug-Fenster.

So ist es korrekt und funktioniert auch:

if (!IPS_SemaphoreEnter($IPS_SELF, 1)) return;
// Skript-Befehle ...
// Skript-Befehle ...
// Skript-Befehle ...
IPS_SemaphoreLeave($IPS_SELF);

Gruß
HJH

Das habe ich auch bemerkt, das ein ‚e‘ fehlt. Dennoch funktionniert es bei mir nicht. Es hat alles nichts gebracht. Das Skript wurde immer dreimal getriggert. Ich konnte da tun und machen was ich will.
Wie dem auch sei. Ich habe eine andere Lösung gefunden. Mit der geht es nun.
Dennoch danke für deine Mühe

Franz

Hi!

Nun habe ich auch nochmal eine Frage zum Semaphore:)

Ich möchte bei meiner Jalousie-Steuerung verhindern, dass während des des Hochfahrens gleichzeitig das Herunterfahren-Script bzw. das 50%- o. 75%-Absenken-Script ausgeführt wird.
Kann ich mit einem Script, die Ausführung eines anderes verhindern?
Das wäre mein Anliegen. Gleichzeitig möchte ich verhindern, dass das selbe Script nochmals getriggert wird.

Anbei das Script zum Jalousie-Öffnen (wie ich es mir dachte, dass es aussehen würde):

<?
/*
*******************************
 IP-SYMCON Event Scripting
*******************************
File     : Rollladen-WZI_auf.ips.php
Trigger  : IPS_Runscript
Interval : 
*/

if (!IPS_SemaphoreEnter($IPS_SELF, 35000)) return; 
if (!IPS_SemaphoreEnter("Rollladen-WZI_ab", 35000));
if (!IPS_SemaphoreEnter("Rollladen-WZI_ab50", 35000));
if (!IPS_SemaphoreEnter("Rollladen-WZI_ab75", 35000));

    SetValueBoolean("WZI_ROLLLADEN_AB50", false);
    SetValueBoolean("WZI_ROLLLADEN_AB75", false);
    FS20_SwitchMode(59443, false);
    IPS_Sleep(2100);
    FS20_SwitchDuration(40264,true,30);
    SetValueBoolean("WZI_ROLLLADEN_ZU_HAND", false);
     
sleep(31);

    SetValueBoolean("WZI_ROLLLADEN_ZU_HAND", false);
    
    IPS_SemaphoreLeave($IPS_SELF);
    IPS_SemaphoreLeave("Rollladen-WZI_ab"); 
    IPS_SemaphoreLeave("Rollladen-WZI_ab50");
    IPS_SemaphoreLeave("Rollladen-WZI_ab75");
?>

Wäre das so richtig, wenn ich, wie im Script zu sehen, die Semaphore so setze?

Was bewirkt nun genau das „return“?
…das der ehemals verhinderte Trigger nach Ablauf der im Semaphore gesetzten Zeit wiederholt wird?

Wenn dem so ist und zwischenzeitlich das Script z.B. 10mal getriggert wurde, gelangen die Trigger in eine „Warteschleife“ oder werden diese verworfen?
Und… wenn dem so ist, würde durch Weglassen des „return“ keine „Warteschleife“ gebildet und einfach nur auf den nächsten regulären Trigger gewartet, also die anderen zwischenzeitlichen Triggerbefehle verworfen?

Ach… dieses Semaphore… vielseitig, aber…:rolleyes:

Hallo!

Das spreche ich mal nun HJH direkt an, da der das scheinbar am besten verstanden und seine Erfahrungen gesammelt hat…

Was meinst du, kann ich das so machen? ^^^
Ist das jetzt so korrekt, mit dem „return“…

if (!IPS_SemaphoreEnter($IPS_SELF, 35000)) return;

oder lieber doch

if (!IPS_SemaphoreEnter($IPS_SELF, 35000)) {return;}

Und nach meiner Beschreibung zum Verhindern der anderen Scriptausführung…

if (!IPS_SemaphoreEnter("anderes Script", 35000));

Würde das so funktionieren, wie ich mir das denke?

Hallo nancilla,

die geschweiften Klammern sind nur dann erforderlich, wenn durch die if-Abfrage mehr als nur eine einzelne Anweisung abgearbeitet werden sollen. Es ist aber auch nicht falsch immer die Klammern zu setzen.

Überlege bitte, was Du erreichen willst:
Falls das Semaphor gesetzt ist, also das Skript „besetzt“ ist, soll ein eventuell nachfolgender Trigger so schnell wie möglich abgewiesen werden. Mit einer derart langen Wartezeit von 35 (!!!) Sekunden erreichst Du genau das Gegenteil. Die Zeitangabe bestimmt, wie lange der Trigger warten muss, bis er es noch einmal versuchen darf. Er hat nur diesen einen Versuch, danach bekommt er keine weitere Gelegenheit mehr. Wenn der Trigger also keine Chance bekommen soll, dann folgt daraus, dass die Wartezeit möglichst kurz sein sollte.

Der Text, der das Semaphor repräsentiert, kann völlig willkürlich gewählt werden, wie z.B. „AQuadratPlusBQuadrat“ oder, was hier sinnvoller wäre: „Zugriff verboten“. Natürlich ist auch der Skriptname gut geeignet, da dieser nicht doppelt vorkommen kann und somit keine Gefahr besteht, dass evtl. ein anderes Skript zufällig denselben Text benutzt und damit unabsichtlich dasselbe Semaphor setzt. Solange dieses Semaphor (also der Text) existiert, kann kein anderer Trigger das Skript, das dieses Semaphor gesetzt hat ausführen. Daher ist es auch sehr wichtig, dass das Semaphor spätestens am Ende des Skripts wieder gelöscht wird.

Der Befehl IPS_SemaphoreEnter(…) kann auf zwei verschieden Weisen benutzt werden:
1. IPS_SemaphoreEnter(„Semaphor-Text“, 1);
Hier wird der Rückgabewert nicht berücksichtigt. Der Befehl dient hier nur dazu das Semaphor zu setzen. Dies kann aus einem beliebigen Skript heraus geschehen.
2. $result = IPS_SemaphoreEnter(„Semaphor-Text“, $delay);
Diese Version wird in dem zu schützenden Skript verwendet. Hier gibt es zwei Fälle zu unterscheiden:
a) Das Semaphor existiert noch nicht. Daher wird es durch diesen Befehl gesetzt und $delay bleibt unberücksichtigt. $result erhält den Wert „TRUE“.
b) Das Semaphor existiert bereits. Der anstehende Trigger wartet die durch $delay vorgegebene Zeit ab. Dann prüft er erneut, ob das Semaphor noch existiert. Ist es inzwischen gelöscht, dann geht es weiter wie bei a). Existiert das Semaphor aber immer noch, dann gibt der Trigger auf und $result erhält den Wert „FALSE“.

In dem von Dir geschilderten Fall ist es aber völlig unnötig mit Semaphoren zu arbeiten. Da es hier nicht wirklich um Gleichzeitigkeit der Trigger geht, kann man die Skriptausführung leicht mit ein paar Variablen regeln. Man muss nicht gleich mit Kanonen auf Spatzen schießen.

Gruß
HJH

Danke HJH!

Wenn du etwas schreibst, dann liest sich das immer wie aus dem Lehrbuch, super!

Nun…

Dann sind die 35 Sekunden schon OK, denn ich schalte meine Rollladenwickler mit FS20 SM4 (Universalempfänger)… reine Bastellösung, da es dafür keine anderen Funkempfänger gibt. Ich habe die OC-Ausgänge des SM4 mit den Tastern der Rollladenwickler parallel geschaltet. Um nun einigermaßen flexibel zu bleiben, beim Heben und Senken der Rollladen schaltet ich das mit SwitchDuration über zwei Kanäle bzw. 2 IPS-Instancen.
Um nun zu verhindern, dass beide Instancen gleichzeitig true werden, habe ich das schon einigermaßen über Variablenabfragen gelöst, aber…

Ich wollte das ganze nun auch über WIIPS schalten, mit nur einer Instanz… true/false. Deshalb habe ich diese weiter Instanz angelegt, extra zur Hansteuerung, welche wiederum, je nach ist „true“ o. „false“ ein Script triggert und über IPS-Runscript die beiden anderen Instanzen switcht. Funktioniert so ganz gut.

Nur… darf kein Switchbefehl für Kanal 1 kommen, während dessen Kanal 2 schon gesetzt ist, oder anders herum, sonst kommt das ganze in einen undefinierten Zustand.
Deshalb möchte ich zur Sicherheit 35 Sekunden warten, bis der nächste Richtungsbefehl ausgeführt wird. Aber ausgeführt werden darf er, wenn auch dann mit Delay.

Also wenn das mit dem Semaphore-Text so ist, wie du das schilderst, dann kann ich $IPS-SELF garnicht benutzen, sondern muss mit eindeutigen Namen arbeiten, wenn ich ein Semaphore auch für andere Scripte setzen will.
Vielen Dank auch für diesen Hinweis!

In dem von Dir geschilderten Fall ist es aber völlig unnötig mit Semaphoren zu arbeiten. Da es hier nicht wirklich um Gleichzeitigkeit der Trigger geht, kann man die Skriptausführung leicht mit ein paar Variablen regeln. Man muss nicht gleich mit Kanonen auf Spatzen schießen.

Schon…, IPS_GetUpdateTime wäre noch eine Möglichkeit, aber ich denke das Semaphore behält das ganze übersichtlicher, da ich sonst mehrere Variablen überprüfen müsste, wenn ich mit SwitchDuration z.B. das Absenken über 50, 75 o. 90% ausführe.

Diese 3 Stellungen sind wetterbedingt… ausgewertet werden Niederschläge, Windstärke, Windrichtung, Sonnenintensität u. Tageszeit.
Je nach dem Senken sich die Rollladen, je auch nach Jahreszeiten (bzw. Tageslängen Abstand: Sonnenauf-/untergang) verschieden reguliert.
Dieser Zustand wird mindestens 15 Minuten beibehalten, sollten sich bis dahin die Bedingungen geändert haben, die zum Absenken geführt haben, dann wird der Rollladen wieder hoch gezogen, oder wenn nicht, dann verbleibt er weitere 15 Minuten in dieser Stellung.
Ich denke, das Semaphore ist in Verbindung dieser Automatik u. Handsteuerung schon der beste Weg.

Bin froh dass es bei allen klappt. Bei mir jedoch geht es leider nicht. Dreimal die selbe Variable getriggert in der selben Millisekunde, und das Skript wird dreimal ausgeführt. Ich kann mich da krumm und quer stellen, es will partout nicht klappen.

Wie dem auch sei. Ich hab eben die Notfall-Lösung gewählt mit IPS_GetUpdateTime. Das geht auch.

Franz

Hallo Franz,

welche Wartezeit hast Du eingestellt und welche Laufzeit hat Dein Skript?

Gruß
HJH

Wartezeit kann ich sogar bis auf 10000 setzen, oder eben auch 20, es geht trotzdem nicht. Warum, frag mich nicht. Das Skript wird dennoch ausgeführt

Laufzeit, …uh oh…das müsste ich dann nachmessen. Ich kann doch unmöglich so dämlich sein, zwei Zeilen per COPY/PASTE einzufügen
Dieses Skript enthält schon meine Modifikation mit IPS_GetUpdateTime

Hier ist das Skript:

/*
*******************************
 IP-SYMCON Event Scripting
*******************************
File     : FHT_TEMP_TENDANCY.ips.php
Trigger  : 
Interval : 
*/
if ($IPS_SENDER == 'Execute')
    {
     echo 'This script cannot be executed manually in order not to disturb functionality of scripts';
     return;
    }
// Tendancy = 1 , Temperature is falling
// Tendancy = 2 , Temperature is static
// Tendancy = 3 , Temperature is rising

// Find out what room is concerned
$room = substr($IPS_VARIABLE, 0, -16);

// Since Sensors get recieved by 3 FHZ's, it happen that the script get triggered 3 times, which will
// give wrong values to the script
$t1 = time();
$t2 = IPS_GetUpdateTime($room."_FHT_LAST_TEMP");

// Execute only, if time is greater than $t1-$t2
if ($t1 - $t2 < 2) return;

// Load Global defines
include_once($room."_GLOBDEF.ips.php");

if ($FHT_actual_temp <  $FHT_last_temp) SetValueInteger($room."_FHT_TEMP_TENDANCY", 1);
if ($FHT_actual_temp == $FHT_last_temp) SetValueInteger($room."_FHT_TEMP_TENDANCY", 2);
if ($FHT_actual_temp >  $FHT_last_temp) SetValueInteger($room."_FHT_TEMP_TENDANCY", 3);

SetValueFloat($room."_FHT_LAST_TEMP", $FHT_actual_temp);

Getriggert wird auf ‚OnUpdate‘ durch die Float Temp.Werte

Franz

Hallo Franz,

es müssen zwei Bedingungen erfüllt sein, damit eine Mehrfach-Triggerung verhindert wird:

  1. Die Wartezeit muss möglichst kurz sein.
  2. Die Skriptlaufzeit muss größer als die Wartezeit sein.

Versuch mal diese Werte:

if (!IPS_SemaphoreEnter($IPS_SELF, 5)) return;
// Skript-Befehle ...
// Skript-Befehle ...
// Skript-Befehle ...
IPS_Sleep(10);
IPS_SemaphoreLeave($IPS_SELF); 

Die Laufzeit wurde hier mit IPS_Sleep(10) so verlängert, dass sie garantiert größer ist als die Wartezeit.
Für die Wartezeit habe ich hier 5ms angesetzt, da Du sagtest, die Trigger kämen alle innerhalb etwa 1ms.

Gruß
HJH

Das ist es ja eben. Auch mit 10 funktionniert es nicht. Irgendwie ist etwas anders, wenn drei FHZ’s denselben Wert kriegen zur gleichen Zeit. Ich krieg es mit dem IPS_Semaphore einfach so nicht hin.

Ich finde deine Ausdauer lobenswert, HJH, aber irgendwie will es nicht.

Franz

Hallo Franz,

drei FHZs sind auch nichts anderes drei gewöhnliche Triggerquellen.
Leider hältst Du Dich aber immer etwas mit Informationen zurück.

Auch mit 10 funktionniert es nicht.

Welchen der Werte hast Du mit „10“ besetzt? Wartezeit oder Laufzeit?

Ist die Bedingung Laufzeit > Wartezeit erfüllt?

Hast Du die fehlende Laufzeit mit IPS_Sleep(…) verlängert?
Mit Laufzeit ist die Zeit gemeint, die zwischen dem Setzen und dem Löschen des Semaphors vergeht.

Ist IPS_SemaphoreEnter(…) der erste Befehl im Skript (also ganz vorne)?
Das Semaphor muss so schnell wie möglich gesetzt werden.
Ist IPS_SemaphoreLeave(…) der letzte Befehl im Skript (also ganz hinten)?
Das Semaphor darf erst ganz zum Schluss gelöscht werden.

Um ganz sicher zu gehen kannst Du die Wartezeit als Variable ($delay) einsetzen und IPS_Sleep(…) immer mit dem dreifachen des Wertes beschicken:

if (!IPS_SemaphoreEnter($IPS_SELF, $delay = 5)) return;
// Skript-Befehle ...
// Skript-Befehle ...
// Skript-Befehle ...
IPS_Sleep($delay*3);
IPS_SemaphoreLeave($IPS_SELF); 

Bitte poste mal die vollständige Version des Semaphor-gesteuerten Skripts.

Gruß
HJH

Hallo Ihr beiden „Kämpfer“
Ich glaube, ihr habt hier was durcheinander gebracht:

Wenn man das Semaphore abfragt, gibt es True zurück, wenn es vorhanden ist, also ein Script bereits läuft.
Die von Dir (HJH) gezeigte Variante läuft aber genau dann.
Oder besser gesagt, sie beendet das Script wenn kein Semaphore gesetzt ist. :wink:

Lest bitte nochmal:http://www.ipsymcon.de/forum/showpost.php?p=32177&postcount=17

Gruß
Fabian

Hallo Fabian,

ich bin nicht der Ansicht hier etwas durcheinander gebracht zu haben.

Meine ausführlichen Tests mit den unten angehängten Skripten bestätigen das von mir beschriebene Verhalten. Wenn die Skriptlaufzeit groß genug ist, wird die Mehrfachausführung zuverlässig verhindert.

Ich bleibe daher vorerst bei dieser Beschreibung:

Der Befehl IPS_SemaphoreEnter(…) kann auf zwei verschieden Weisen benutzt werden:
1. IPS_SemaphoreEnter(„Semaphor-Text“, 1);
Hier wird der Rückgabewert nicht berücksichtigt. Der Befehl dient hier nur dazu das Semaphor zu setzen. Dies kann aus einem beliebigen Skript heraus geschehen.
2. $result = IPS_SemaphoreEnter(„Semaphor-Text“, $delay);
Diese Version wird in dem zu schützenden Skript verwendet. Hier gibt es zwei Fälle zu unterscheiden:
a) Das Semaphor existiert noch nicht. Daher wird es durch diesen Befehl gesetzt und $delay bleibt unberücksichtigt. $result erhält den Wert „TRUE“.
b) Das Semaphor existiert bereits. Der anstehende Trigger wartet die durch $delay vorgegebene Zeit ab. Dann prüft er erneut, ob das Semaphor noch existiert. Ist es inzwischen gelöscht, dann geht es weiter wie bei a). Existiert das Semaphor aber immer noch, dann gibt der Trigger auf und $result erhält den Wert „FALSE“.

Meine Versuche stützen sich dabei auf Paresy’s sehr knappe Beschreibung, die eigentlich nur eine einzige sinnvolle Interpretation zulässt:
TRUE:

  • a) wenn das Semaphor nicht gesetzt ist oder
  • b) wenn die Wartezeit abgelaufen und das Semaphor inzwischen wieder gelöscht ist
    FALSE:
  • nur wenn die Wartezeit abgelaufen und das Semaphor immer noch (oder schon wieder) gesetzt ist

Der Fall, den Du beschreibst, dass das Semaphor gesetzt ist, aber die Wartezeit noch läuft, existiert nicht, da der Wartevorgang im Hintergrund abläuft und für PHP gar nicht in Erscheinung tritt.

Gruß
HJH

SemaTest.zip (821 Bytes)

So, hier ist das Skript wie es NICHT funktionniert:

/*
*******************************
 IP-SYMCON Event Scripting
*******************************
File     : HMS_MOISTURE_TENDANCY.ips.php
Trigger  : 
Interval : 
*/
if ($IPS_SENDER == 'Execute')
    {
     echo 'This script cannot be executed manually in order not to disturb functionality of scripts';
     return;
    }

if (!IPS_SemaphoreEnter($IPS_SELF, 1)) return;
    
// Tendancy = 1 , Moisture is falling
// Tendancy = 2 , Moisture is static
// Tendancy = 3 , Moisture is rising

// Find out what room is concerned
$room = substr($IPS_VARIABLE, 0, -13);

// Load Global defines
include_once($room."_GLOBDEF.ips.php");

if ($room_moisture <  $room_moisture_last) SetValueInteger($room."_HMS_MOISTURE_TENDANCY", 1);
if ($room_moisture == $room_moisture_last) SetValueInteger($room."_HMS_MOISTURE_TENDANCY", 2);
if ($room_moisture >  $room_moisture_last) SetValueInteger($room."_HMS_MOISTURE_TENDANCY", 3);

SetValueFloat($room."_HMS_MOISTURE_LAST", $room_moisture);

// IPS_Sleep(10);  // Hier könnte eben noch diese Wartezeit mit eingesetzt werden

IPS_SemaphoreLeave($IPS_SELF);

Senden mehrere FHZ’s, wird das Skript auch dementsprechend oft ausgeführt.
Dabei kann ich IPS_Sleep so hoch setzen wie ich will, auch bei IPS_Semaphore, es bringt alles nichts.

Franz

Hallo Franz,

hast du denn schonmal die Variante, die Fabian vorschlägt, probiert?
Also ohne „!“
Das müsste dann ja klappen, wenn es nun so ist…

@HJH:

Asche auf mein Haupt! Natürlich hast Du recht!

Hätte ich doch nur mal in meine Scripte geschaut… :eek: das habe ich bei nahezu jedem Script genauso drin. :o

@Franz:
Prüf mal ob $IPS_SELF etwas liefert, nicht das Dir ein Update fehlt.
ansonsten müsste es doch irgendwelche Fehler im Log geben

Ich bin auch der Meinung, dass es so laufen müsste. Hier eine Variante aus meinen Scripten:

if (!IPS_SemaphoreEnter("fht_control_".$level."_run",1)){
      echo ("#### Script is running yet! ####");
      return;
   }

schreib doch mal ein paar debug-echos in die einzelnen Abfragen
z.B.

echo $IPS_SELF;

Grüße
Fabian