'...','port'=>...], ...] private AudioService $audio; public function onWorkerStart(Worker $worker): void { $this->port = (int)(getenv('TIMER_PORT') ?: 10000); $this->maxValue = (int)(getenv('TIMER_MAX') ?: 6); $this->intervalSec = (float)(getenv('TIMER_INTERVAL') ?: 1); $this->loadTexts(); $this->targets = $this->loadIps(); $this->audio = new AudioService(); echo "Timer process is running.\n"; echo "目标: "; foreach ($this->targets as $t) { echo "{$t['ip']}:{$t['port']} "; } echo "\n最大值: {$this->maxValue}, 间隔: {$this->intervalSec}s\n"; $value = 1; $timerCallback = function () use (&$value) { $text = $this->texts[$value] ?? "当前: {$value}"; $okImg = $this->sendImage($value); $status = $okImg ? '图✓' : '图✗'; echo "\r[{$value}] {$status} {$text}"; $okAudio = $this->audio->sendText($text); $status .= ' ' . ($okAudio ? '音✓' : '音✗'); echo "\r[{$value}] {$status} {$text}\n"; $value = $value >= $this->maxValue ? 1 : $value + 1; }; // 立即执行一次,再定时 $timerCallback(); WorkermanTimer::add($this->intervalSec, $timerCallback); } /** * 加载目标 IP 列表 * 优先检查 TIMER_IPS 环境变量(逗号分隔,可带端口如 192.168.1.1:10001) * 否则回退到单个 TIMER_IP + TIMER_PORT */ private function loadIps(): array { $targets = []; $ipsEnv = getenv('TIMER_IPS'); if ($ipsEnv !== false && trim($ipsEnv) !== '') { // 多 IP 模式 $parts = explode(',', $ipsEnv); foreach ($parts as $part) { $part = trim($part); if ($part === '') continue; if (strpos($part, ':') !== false) { [$ip, $portStr] = explode(':', $part, 2); $targets[] = [ 'ip' => trim($ip), 'port' => (int)trim($portStr) ]; } else { $targets[] = [ 'ip' => $part, 'port' => $this->port ]; } } } else { // 兼容单 IP 旧配置 $ip = getenv('TIMER_IP') ?: '192.168.42.1'; $targets[] = [ 'ip' => $ip, 'port' => $this->port ]; } return $targets; } /** * 向所有目标发送 HEX 数据包 * @return bool 全部发送成功返回 true,任一失败返回 false */ private function sendImage(int $variable): bool { $data = self::HEX_DATA; $data[self::VAR_POS] = ($variable + self::VAR_OFFSET) & 0xFF; $packedData = pack('C*', ...$data); $allSuccess = true; foreach ($this->targets as $target) { try { $socket = @fsockopen($target['ip'], $target['port'], $errno, $errstr, 5); echo "\r图> 尝试连接 [{$target['ip']}:{$target['port']}]"; if (!$socket) { echo "\r图> 连接失败 [{$target['ip']}:{$target['port']}]: {$errstr} ({$errno})\n"; $allSuccess = false; continue; } echo "\r图> 已连接 [{$target['ip']}:{$target['port']}]"; fwrite($socket, $packedData); fflush($socket); fclose($socket); }catch (\Exception $exception){ echo "发送数据失败 [{$target['ip']}:{$target['port']}]: {$exception->getMessage()}\n"; $allSuccess = false; } } return $allSuccess; } private function loadTexts(): void { for ($i = 1; $i <= $this->maxValue; $i++) { $t = getenv('TIMER_TEXT_' . $i); if ($t !== false && $t !== '') { $this->texts[$i] = $t; } } } }