Skip to content
Snippets Groups Projects
InitializeConfig.php 5.93 KiB
Newer Older
<?php

namespace App\Subroutines;

use App\Application;
use App\Concerns\InteractiveConsole;
use App\Model\Fan\HwmonFan;
use App\Service\Utils;
use App\Subroutines\Fan\EditFan;
use Hugga\Console;
use Hugga\Input\Question\Choice;
use Hugga\Input\Question\Confirmation;

class InitializeConfig
{
    use InteractiveConsole;

    /** @var Application */
    protected $app;

    public function __construct(Application $app, Console $console)
    {
        $this->app = $app;
        $this->console = $console;
    }

    public function main(): int
    {
        $fanConfig = $this->app->fanConfig;

        $this->info('No fans configured. Detecting fans...');
        $fans = $this->detectControllableFans();
        if (count($fans) > 0) {
            foreach ($fans as $fan) {
                $this->line('');
                $this->info('Fan ' . $fan->name);
                $start = $this->detectStartValue($fan);
                $fan->setStartValue($start);
                $this->table([$fan->toRow()], ['Name', 'hwmon', 'Fan', 'PWM', 'start', 'max']);
                do {
                    $answer = $this->ask(new Choice([
                        'skip fan (don\'t include fan in config)',
                    ], 'What next?', 'edit fan'));
                    switch ($answer) {
                        case 'edit fan':
                            $this->app->get(EditFan::class)->main($fan);
                        case 'add fan':
                            $fanConfig->addFan($fan);
                            break 2;
                        case 'skip fan':
                            break 2;
                    }
                } while (true);
            }

            $fanConfig->save();
            return 0;
        }
        $this->error('We could not find any fan to control.');
        return 1;
    }

    /**
     * @return array|HwmonFan[]
     */
    protected function detectControllableFans(): array
    {
        $fans = [];
        $inputs = glob(HWMON_PATH . '/hwmon*/fan*_input');
        $inputs = array_reduce($inputs, function ($inputs, $input) {
            if (!preg_match('~(hwmon(\d+)/(fan\d+))_input$~', $input, $match)) {
                return $inputs;
            }

            $hwmon = trim(file_get_contents(HWMON_PATH . '/hwmon' . $match[2] . '/name'));
            $fan = $match[3];
            $pwm = preg_replace('~fan(\d+)~', 'pwm$1', $fan);
            $name = $hwmon . '/' . $fan;
            if (!file_exists(HWMON_PATH . '/hwmon' . $match[2] . '/' . $pwm)) {
                return $inputs;
            }

            $options = [
                'hwmon' => $hwmon,
                'fan' => $fan,
                'pwm' => $pwm,
            ];
            $inputs[$name] = [
                'fan' => new HwmonFan($name, $options),
                'options' => $options,
                'hwmon' => 'hwmon' . $match[2],
            ];
            return $inputs;
        }, []);

        $unknownInputs = [];
        foreach ($inputs as $name => $input) {
            [$min, $max] = $this->testPwm($input['fan']);
            if ($max === 0 || $min > $max*0.4) {
                $unknownInputs[$name] = $input;
                continue;
            }
            $this->line(sprintf('%s at full speed: %d; stopped: %d', $name, $max, $min));
            $fans[] = $input['fan'];
        }

        if (count($unknownInputs) > 0) {
            $this->info(PHP_EOL . 'Some fans could not be assigned to a pwm. Trying remaining pwms...');
            $assignedPwms = array_map(fn(HwmonFan $fan) => $fan->toArray()['options']['pwm'], $fans);
            $pwms = array_reduce(glob(HWMON_PATH . '/hwmon*/pwm*'), function ($pwms, $pwm) use ($assignedPwms) {
                if (!preg_match('~(hwmon(\d+)/(pwm\d+))$~', $pwm, $match) || in_array(basename($pwm), $assignedPwms)) {
                    return $pwms;
                }

                $hwmon = $match[2];
                if (!isset($pwms[$hwmon])) {
                    $pwms[$hwmon] = [];
                }
                $pwms[$hwmon][] = basename($pwm);

                return $pwms;
            }, []);
            foreach ($unknownInputs as $id => $input) {
                if (!isset($pwms[$input['hwmon']])) {
                    continue;
                }
                foreach ($pwms[$input['hwmon']] as $pwm) {
                    $name = $input['fan']->name;
                    $options = $input['options'];
                    if ($options['pwm'] === $pwm) {
                        continue;
                    }

                    $options['pwm'] = $pwm;
                    $fan = new HwmonFan($name, $options);

                    [$min, $max] = $this->testPwm($fan);
                    if ($max === 0 || $min > $max*0.4) {
                        continue;
                    }

                    $fans[] = $fan;
                    continue 2;
                }
                $this->info($id . ' not connected or not controllable');
            }
        }

        return $fans;
    }

    protected function testPwm(HwmonFan $fan): array
    {
        $fan->setPwmAndWait(255);
        $fullSpeed = $fan->getCurrentSpeed();

        if ($fullSpeed === 0) {
            $fan->resetState();
            return [0, 0];
        }

        $fan->setPwmAndWait(0);
        $stopSpeed = $fan->getCurrentSpeed();

        $fan->resetState();
        return [$stopSpeed, $fullSpeed];
    }

    protected function detectStartValue(HwmonFan $fan): int
    {
        $this->info('Detecting start value for ' . $fan->name);
        $fan->setPwmAndWait(0);

        $minSpeed = $fan->getCurrentSpeed() * 1.1;
        $pwm = 0;
        do {
            $fan->setPwmAndWait($pwm += 5);
            $currentSpeed = $fan->getCurrentSpeed();
            $this->line(sprintf('%d rpm with pwm value %d', $currentSpeed, $pwm));
        } while ($minSpeed >= $currentSpeed);

        $fan->resetState();
        return $pwm;
    }
}