91 lines
3.8 KiB
PHP
91 lines
3.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
// Development-only native timer fixture. No device, manager or variable actions.
|
|
class TimerLifecycleProbe extends IPSModule
|
|
{
|
|
public function Create(): void
|
|
{
|
|
parent::Create();
|
|
$this->RegisterPropertyInteger('ReadWorkMs', 1500);
|
|
$this->RegisterPropertyInteger('ReportWorkMs', 1500);
|
|
$this->RegisterPropertyString('LifecycleMode', 'baseline');
|
|
$this->RegisterAttributeInteger('ReadRuns', 0);
|
|
$this->RegisterAttributeInteger('ReportRuns', 0);
|
|
$this->RegisterAttributeInteger('CallbackEnds', 0);
|
|
$this->RegisterAttributeInteger('ScriptEnds', 0);
|
|
$this->RegisterTimer('Meldezyklus', 0, "IPS_RequestAction(\$_IPS['TARGET'], 'Report', 0); ENLTMR_MarkScriptEnd(\$_IPS['TARGET']);");
|
|
$this->RegisterTimer('Rueckmeldesampler', 0, "IPS_RequestAction(\$_IPS['TARGET'], 'Read', 0); ENLTMR_MarkScriptEnd(\$_IPS['TARGET']);");
|
|
}
|
|
|
|
public function ApplyChanges(): void
|
|
{
|
|
parent::ApplyChanges();
|
|
$lock = 'ENELIX.TimerProbe.Battery.' . $this->InstanceID;
|
|
if (!IPS_SemaphoreEnter($lock, 1000)) throw new RuntimeException('Probe battery busy');
|
|
IPS_SemaphoreLeave($lock);
|
|
$this->SetTimerInterval('Meldezyklus', 5000);
|
|
$this->SetTimerInterval('Rueckmeldesampler', 1000);
|
|
$this->SetStatus(102);
|
|
}
|
|
|
|
public function Destroy(): void
|
|
{
|
|
IPS_LogMessage('ENELIXTimerLifecycleProbe', 'Destroy entry ' . $this->InstanceID);
|
|
// Isolated experiment only: determine whether unload can drain native timers.
|
|
if (IPS_InstanceExists($this->InstanceID) && $this->ReadPropertyString('LifecycleMode') === 'drain') {
|
|
$this->SetTimerInterval('Meldezyklus', 0);
|
|
$this->SetTimerInterval('Rueckmeldesampler', 0);
|
|
$deadline = microtime(true) + 2.5;
|
|
do {
|
|
$running = false;
|
|
foreach (IPS_GetTimerList() as $id) {
|
|
$timer = IPS_GetTimer($id);
|
|
if ($timer['InstanceID'] === $this->InstanceID && $timer['Running']) $running = true;
|
|
}
|
|
if ($running) usleep(20000);
|
|
} while ($running && microtime(true) < $deadline);
|
|
IPS_LogMessage('ENELIXTimerLifecycleProbe', 'Destroy drain running=' . (int) $running);
|
|
}
|
|
parent::Destroy();
|
|
}
|
|
|
|
public function Tick(string $ident): void
|
|
{
|
|
$this->RequestAction($ident, 0);
|
|
}
|
|
|
|
public function StopProbe(): void
|
|
{
|
|
$this->SetTimerInterval('Meldezyklus', 0);
|
|
$this->SetTimerInterval('Rueckmeldesampler', 0);
|
|
}
|
|
|
|
public function RequestAction($Ident, $Value): void
|
|
{
|
|
if (!in_array($Ident, ['Read', 'Report'], true)) throw new InvalidArgumentException('Unknown probe action');
|
|
$lock = 'ENELIX.TimerProbe.' . ($Ident === 'Read' ? 'Feedback.' : 'Battery.') . $this->InstanceID;
|
|
if (!IPS_SemaphoreEnter($lock, 0)) return;
|
|
try {
|
|
usleep(max(0, min(2000, $this->ReadPropertyInteger($Ident . 'WorkMs'))) * 1000);
|
|
$this->WriteAttributeInteger($Ident . 'Runs', $this->ReadAttributeInteger($Ident . 'Runs') + 1);
|
|
} finally {
|
|
IPS_SemaphoreLeave($lock);
|
|
}
|
|
$this->WriteAttributeInteger('CallbackEnds', $this->ReadAttributeInteger('CallbackEnds') + 1);
|
|
}
|
|
|
|
public function MarkScriptEnd(): void
|
|
{
|
|
$this->WriteAttributeInteger('ScriptEnds', $this->ReadAttributeInteger('ScriptEnds') + 1);
|
|
}
|
|
|
|
public function ReadProbeState(): string
|
|
{
|
|
return json_encode(['readRuns' => $this->ReadAttributeInteger('ReadRuns'),
|
|
'callbackEnds' => $this->ReadAttributeInteger('CallbackEnds'),
|
|
'scriptEnds' => $this->ReadAttributeInteger('ScriptEnds'),
|
|
'reportRuns' => $this->ReadAttributeInteger('ReportRuns')], JSON_THROW_ON_ERROR);
|
|
}
|
|
}
|