Semaphore in foreach Schleife

Ich möchte mehrere Befehle in Folge absetzten. Dazu habe ich zwei Funktionen eine Funktion die sendet und eine zweite Funktion die prüft wann der Request angekommen ist und die Variable in IP-Symcon aktualisiert wurde und mir dann den Status zurück gibt.
Da es keinen festen Zeitrahmen gibt wie lange der Response braucht und ich sicher stellen will das die beiden Funktionen nacheinander abgearbeitet werden wird Semaphore benutzt.

Die Funktionen sehen wie folgt aus:

Aufrufende Funktion ($Command wird aus $Ident berechnet, ist aus der Funktion gekürzt)


$timestamp = time();
$state = updatestates($timestamp, $ObjektID, $Ident);

function updatestates($timestamp, $ObjektID, $Ident)
 {
 $Command = $Ident;
$request = $Command.chr(63);
if (IPS_SemaphoreEnter("AVRStatusRequest", 5000))
{
//Ruft Funktion zum Senden auf
		DAVRT_SendCommand(34489 /*[Modultest\Denon AV Receiver Telnet Control]*/, $request);
	

    //Semaphore wieder freigeben!
    IPS_SemaphoreLeave("AVRStatusRequest");
}
else
{
    // ...Keine ausführung Möglich. Ein anderes Skript nutzt den "KritischenPunkt"
    // für länger als 5 Sekunden, sodass unsere Wartezeit überschritten wird.
    echo "Problem send 
";
}
 if (IPS_SemaphoreEnter("AVRStatusRequest", 5000))
{
//Response abwarten und auslesen   
 $state = getresponse ($ObjektID, $timestamp);
return $state;	
    //Semaphore wieder freigeben!
    IPS_SemaphoreLeave("AVRStatusRequest");
    
}
else
{
    // ...Keine ausführung Möglich. Ein anderes Skript nutzt den "KritischenPunkt"
    // für länger als 5 Sekunden, sodass unsere Wartezeit überschritten wird.
    echo "Problem get 
";
    $state = "Error";
    return $state;
    
    
}
 
 }


Request prüfen


function getresponse ($ObjektID, $timestamp)
{
   	
      $i = 0;
		do
		{
		IPS_Sleep(50);
		$updatetimestamp = IPS_GetVariable($ObjektID)["VariableUpdated"];
		$i++;
		}
		while($updatetimestamp <= $timestamp);
		$lastupdate = date("H:i:s", $updatetimestamp);
		//echo "Var Update um: ".$lastupdate." Uhr 
";
		$state = GetValueFormatted($ObjektID);
		return $state;
	
}

So weit funktioniert alles auch sauber wenn ich updatestates($timestamp, $ObjektID, $Ident) einmal aufrufe.

Jetzt habe ich aber das Problem das ich nicht einen Befehl senden will sondern einen ganzen Array nacheinander.

Dazu liegt jetzt updatestates($timestamp, $ObjektID, $Ident) in einer Foreach Schleife


foreach ($AVRCommands as $ObjektID => $Ident)
		{
		   $timestamp = time();
		   $state = updatestates($timestamp, $ObjektID, $Ident);

 		}

Nun läuft die Schleife einmal sauber durch ab dem zweiten Durchgang kann der Semaphore nicht mehr betreten werden. Irgendwo habe ich da einen Denkfehler. Kann mir da jemand auf die Sprünge helfen wie die foreach Schleife auszusehen hat das die Befehle sauber einzeln nacheinander abgearbeitet werden? Komme ich da irgendwo dann in zeitliche Probleme bei der Ausführung wenn der Array groß ist wie ist dies eventuell alternativ zu lösen?


    return $state;    
    //Semaphore wieder freigeben!
    IPS_SemaphoreLeave("AVRStatusRequest");

Ein return Statement beendet die Ausführung der Funktion sofort. Das IPS_SemaphoreLeave wird somit nie aufgerufen.

paesy

Ah ok, vielen Dank für den Hinweis.

Würde das dann so funktionieren?


function bla()
{
if (IPS_SemaphoreEnter("AVRStatusRequest", 5000))
{
//Response abwarten und auslesen   
 $state = getresponse ($ObjektID, $timestamp);

    //Semaphore wieder freigeben!
    IPS_SemaphoreLeave("AVRStatusRequest");
    
}
else
{
    // ...Keine ausführung Möglich. Ein anderes Skript nutzt den "KritischenPunkt"
    // für länger als 5 Sekunden, sodass unsere Wartezeit überschritten wird.
    echo "Problem get 
";
    $state = "Error";
}
return $state; 
}   

Sieht auf den ersten Blick gut aus.

paresy

Ich habe jetzt das umgestellt habe aber leider immer noch Probleme mit der Abarbeitung des Scripts.

Wenn ich wenige Befehle schicke, der Array also klein ist funktioniert es.

Dann bekomme ich z.B.:


string(11) "PSMULTEQ: ?"
Var Update um: 09:57:19 Uhr 
string(3) "Off"
string(9) "PSMODE: ?"
Var Update um: 09:57:20 Uhr 
string(6) "Cinema"

Es braucht also in der Regel eine Sekunde zwischen dem Absenden des Befehls und der Änderung der Variable in IPS durch den Response.

Sobald der Array jetzt aber größerer wird hängt das Script. Der Semaphore ist blockiert und das Skript hängt dauerhaft unter den PHP Threads.

Das Test Script sieht so aus:


$AVRCommands = array(
			//"51885" => "PSCEN",
			//"44694" => "PSDIM",
			//"24914" => "VSAUDIO",
			"38534" => "PSMULTEQ",
			//"37854" => "PSPHG",
 			//"12262" => "PSREFLEV",
  			//"39670" => "PSRSZ",
         //"33661" => "PSSTW",
         //"54742" => "PSSTH",
         //"33941" => "PSSB",
         //"29885" => "PSMODE",
         //"57450" => "VSVST",
         //"50103" => "SV",
         //"31051" => "PVCN",
         //"33661" => "PSSTW",
         //"54742" => "PSSTH",
         "29885" => "PSMODE"
			 );

foreach ($AVRCommands as $ObjektID => $Ident)
		{
		   $timestamp = time();
		   $state = updatestates($timestamp, $ObjektID, $Ident);
         	   var_dump($state);
		}

function updatestates($timestamp, $ObjektID, $Ident)
 {
	 if ($Ident == "CVFL" || $Ident == "CVFR" || $Ident == "CVC" || $Ident == "CVSW" || $Ident == "CVSL" || $Ident == "CVSR")
	 {
	  $Command = "CV";
	 }
	 elseif($Ident == "PSDYNVOL")//Dynamic Volume
	 {
     $Command = "PSDYNVOL ";
	 }
	 elseif($Ident == "PSVOLLEV")//Dolby Volume Leveler
	 {
     $Command = "PSVOLLEV ";
	 }
	  elseif($Ident == "PSVOLMOD")//Dolby Volume Modeler
	 {
     $Command = "PSVOLMOD ";
	 }
	 elseif($Ident == "PSDOLVOL")//Dolby Volume
	 {
     $Command = "PSDOLVOL ";
	 }
	 elseif($Ident == "PSPAN")//Panorama
	 {
     $Command = "PSPAN ";
	 }
	 elseif($Ident == "PSMULTEQ")//PSMULTEQ
	 {
     $Command = "PSMULTEQ: ";
	 }
	 elseif($Ident == "PVCM")//Chroma Level
	 {
     $Command = "PVCM ";
	 }
	 elseif($Ident == "PSSWR")//Subwoofer
	 {
     $Command = "PSSWR ";
	 }
	 elseif($Ident == "PSEFF")//Effekt
	 {
     $Command = "PSEFF ";
	 }
	 elseif($Ident == "PVCN")//Contrast
	 {
     $Command = "PVCN ";
	 }
	 elseif($Ident == "PVENH")//Enhancer
	 {
     $Command = "PVENH ";
	 }
	 elseif($Ident == "PSSWR")//Subwoofer
	 {
     $Command = "PSSWR ";
	 }
	 elseif($Ident == "PSEFF") //Effekt Level
	 {
     $Command = "PSEFF ";
	 }
	 elseif($Ident == "VSVST") //Vertical Stretch
	 {
     $Command = "VSVST ";
	 }
	 elseif($Ident == "PSRSZ") //Room Size
	 {
     $Command = "PSRSZ ";
	 }
	 elseif($Ident == "PSCINEMA_EQ") //Cinema EQ
	 {
     $Command = "PSCINEMA EQ. ";
	 }
	 elseif($Ident == "PSCINEMA_EQ") //Tone CTRL
	 {
     $Command = "PSCINEMA EQ. ";
	 }
	 elseif($Ident == "PSDELAY") //Audio Delay
	 {
     $Command = "PSDELAY ";
	 }
	 elseif($Ident == "MSQUICK") //MSQUICK
	 {
     $Command = "MSQUICK ";
	 }
	 elseif($Ident == "PSSB") //Surround Back Mode
	 {
     $Command = "PSSB: ";
	 }
	 elseif($Ident == "PVBR") //Brightness
	 {
     $Command = "PVBR ";
	 }
	 elseif($Ident == "PVHUE") //Hue
	 {
     $Command = "PVHUE ";
	 }
	 elseif($Ident == "PSATT") //Subwoofer ATT
	 {
     $Command = "PSATT ";
	 }
	 elseif($Ident == "PSSTW") //Stage Width
	 {
     $Command = "PSSTW ";
	 }
	 elseif($Ident == "PSSTH") //Stage Height
	 {
     $Command = "PSSTH ";
	 }
	 elseif($Ident == "PSMODE") //Surround Play Mode
	 {
     $Command = "PSMODE: ";
	 }
	 elseif($Ident == "PSAFD") //AFDM
	 {
     $Command = "PSAFD ";
	 }
	 elseif($Ident == "PSSP") // Speaker Output Front
	 {
     $Command = "PSSP".chr(58).chr(32);
	 }
	 elseif($Ident == "VSASP") //ASP
	 {
     $Command = "VSASP ";
	 }
	 elseif($Ident == "PSCEI") //Center Image
	 {
     $Command = "PSCEI ";
	 }
	 elseif($Ident == "PVCN") //Contrast
	 {
     $Command = "PVCN ";
	 }
	 elseif($Ident == "PSREFLEV") //Reference Level
	 {
     $Command = "PSREFLEV ";
	 }
	 elseif($Ident == "VSSC") //Resolution
	 {
     $Command = "VSSC ";
	 }
	 elseif($Ident == "VSSCH") //Resolution HDMI
	 {
     $Command = "VSSCH ";
	 }
	 elseif($Ident == "VSAUDIO") //HDMI Audio Output
	 {
     $Command = "VSAUDIO ";
	 }
	 elseif($Ident == "PSEFF") //Effect Level
	 {
     $Command = "PSEFF ";
	 }
	 elseif($Ident == "PSEFF_O") //Effect
	 {
     $Command = "PSEFF ";
	 }
	 elseif($Ident == "PSDSX") //Audyssey DSX
	 {
     $Command = "PSDSX ";
	 }
	 elseif($Ident == "PSDYNEQ") //Dynamic EQ
	 {
     $Command = "PSDYNEQ ";
	 }
	 elseif($Ident == "PSFH") //Front Hight
	 {
     $Command = "PSFH: ";
	 }
	 elseif($Ident == "PSCEN") //Center Width
	 {
     $Command = "PSCEN ";
	 }
	 elseif($Ident == "PSDIM") //Dimension
	 {
     $Command = "PSDIM ";
	 }
	 elseif($Ident == "PSPHG") //PLIIZ Height Gain
	 {
     $Command = "PSPHG ";
	 }
	 elseif($Ident == "PSBAS") //Bass Level
	 {
     $Command = "PSBAS ";
	 }
	 elseif($Ident == "PSTRE") //Treble Level
	 {
     $Command = "PSTRE ";
	 }
	 elseif($Ident == "PSLFE") //LFE Level
	 {
     $Command = "PSLFE ";
	 }
	 elseif($Ident == "PVDNR") //Digital Noise Reduction
	 {
     $Command = "PVDNR ";
	 }
	 elseif($Ident == "VSMONI") //HDMI Monitor
	 {
     $Command = "VSMONI ";
	 }
	 elseif($Ident == "VSVPM") //Video Processing Mode
	 {
     $Command = "VSVPM ";
	 }
	 else
	 {
	 $Command = $Ident;
	}

 $request = $Command.chr(63);
 var_dump($request);

 //Semaphore setzen
        if (lock("StatusUpdate"))
        {
        // Daten senden
	        try
	        {
	            if ($Ident !== "PSEFF_O")
						{
							DAVRT_SendCommand(34489 /*[Modultest\Denon AV Receiver Telnet Control]*/, $request);
						}
					elseif($Ident == "PSEFF_O")
						{

							$request = "PSEFF".chr(63);
							DAVRT_SendCommand(34489 /*[Modultest\Denon AV Receiver Telnet Control]*/, $request);
						}
	        }
	        catch (Exception $exc)
	        {
	            // Senden fehlgeschlagen
	            $this->unlock("StatusUpdate");
	            throw new Exception($exc);
	        }
        unlock("StatusUpdate");
        }
        else
        {
			echo "Can not send to parent 
";
			unlock("StatusUpdate");
			//throw new Exception("Can not send to parent",E_USER_NOTICE);
		  }
 			// Response abholen
        if (lock("StatusUpdate"))
        {
	        try
	        {
	            $state = getresponse ($ObjektID, $timestamp);
	        }
	        catch (Exception $exc)
	        {
	            // Senden fehlgeschlagen
	            unlock("StatusUpdate");
	            throw new Exception($exc);
	        }
        unlock("StatusUpdate");
         return $state;
        }
        else
        {
         	echo "Can not get response 
";
         	unlock("StatusUpdate");
				//throw new Exception("Can not get response",E_USER_NOTICE);
		  }

 }

function getresponse ($ObjektID, $timestamp)
{
   $Ident = IPS_GetObject($ObjektID)["ObjectIdent"];
	if ($Ident !== "PSEFF_O")
	{
      $i = 0;
		do
		{
		IPS_Sleep(50);
		$updatetimestamp = IPS_GetVariable($ObjektID)["VariableUpdated"];

		//echo $i."
";
		$i++;
		}
		while($updatetimestamp <= $timestamp);
		$lastupdate = date("H:i:s", $updatetimestamp);
		echo "Var Update um: ".$lastupdate." Uhr 
";
		$state = GetValueFormatted($ObjektID);
		return $state;
	}
	elseif ($Ident == "PSEFF_O")
	{
      $i = 0;
		do
		{
		IPS_Sleep(50);
		$idPSEFF = IPS_GetObjectIDByIdent("PSEFF", 34489 /*[Modultest\Denon AV Receiver Telnet Control]*/);
		$updatetimestamp = IPS_GetVariable($idPSEFF)["VariableUpdated"];

		$i++;
		}
		while($updatetimestamp <= $timestamp);
		$lastupdate = date("H:i:s", $updatetimestamp);
		echo "Var Update um: ".$lastupdate." Uhr 
";
		$state = GetValue($idPSEFF);
		if ($state == 0.0)
		{
		   $state = false;
		}
		else
		{
		   $state = true;
		}
		return $state;
	}
}






################## SEMAPHOREN Helper  - private

    function lock($ident)
    {
        for ($i = 0; $i < 3000; $i++)
        {
            if (IPS_SemaphoreEnter("DENONAVRT_" . (string) $ident, 1))
				//if (IPS_SemaphoreEnter("DENONAVRT_" . (string) $this->InstanceID . (string) $ident, 1))
            {
                return true;
            }
            else
            {
                IPS_Sleep(mt_rand(1, 5));
            }
        }
        return false;
    }

    function unlock($ident)
    {
         IPS_SemaphoreLeave("DENONAVRT_" . (string) $ident);
		  	//IPS_SemaphoreLeave("DENONAVRT_" . (string) $this->InstanceID . (string) $ident);
    }


Hat jemand eine Idee warum das Script hängt?

Kann ich einen hängenden PHP Thread über die ThreadID einzeln killen bzw. beenden ohne IPS komplett schießen zu müssen?

Desweiteren bekomme ich wohl auch ein Problem bei vielen Variablen mit dem Timeout wenn es 1 Sekunde braucht bis jeweils der aktuelle Status in IPS in der Variable gesetzt wird. Ich müsste initial nach dem Starten des Moduls einmal für alle angelegten Variablen den Status abfragen. Hat irgendjemand eine Idee wie man dies am besten macht ohne ein Timeout Problem zu bekommen?