Skip to content
Snippets Groups Projects
Verified Commit 87021529 authored by Thomas Flori's avatar Thomas Flori
Browse files

add fan selection and subroutine for edit fan

parent efdce0d6
No related branches found
No related tags found
No related merge requests found
......@@ -3,8 +3,10 @@
namespace App\Command;
use App\Application;
use App\Model\Fan\HwmonFan;
use App\Model\Fan;
use App\Service\SystemCtl;
use App\Service\Utils;
use App\Subroutines\Fan\EditFan;
use App\Subroutines\InitializeConfig;
use GetOpt\GetOpt;
use Hugga\Console;
......@@ -34,6 +36,12 @@ class Config extends AbstractCommand
return 1;
}
$ranBefore = SystemCtl::isActive('fan-ctrl');
if (!SystemCtl::stop('fan-ctrl')) {
$this->warn('fan-ctrl could not be stopped');
return 1;
}
$fanConfig = $this->app->fanConfig;
try {
$fanConfig->readConfig();
......@@ -60,28 +68,37 @@ class Config extends AbstractCommand
$answer = $this->ask($actions);
switch ($answer) {
case 'edit a fan':
$fan = $this->selectFan($fanConfig->getFans());
if ($fan = $this->selectFan($fanConfig->getFans())) {
$this->app->get(EditFan::class)->main($fan);
}
break;
case 'exit without saving':
break 2;
case 'save and exit':
$fanConfig->save();
break 2;
default:
$this->warn('not implemented');
break;
}
} while (true);
// - edit a fan
// - add a sensor
// - edit a sensor
// - add a rule
// - edit a rule
// - save and leave
// - leave without saving
if ($ranBefore) {
SystemCtl::start('fan-ctrl');
}
return 0;
}
protected function selectFan(array $fans): ?Fan
{
$choices = array_keys($fans);
$choices[] = 'cancel';
$question = new Choice($choices);
$question->returnKey();
$answer = $this->ask($question);
return $choices[$answer] === 'cancel' ? null : $fans[$choices[$answer]];
}
}
......@@ -152,15 +152,7 @@ class FanConfig
$console->info('Fans');
$table = new Table($console, array_map(function (Fan $fan) {
if ($fan instanceof Fan\HwmonFan) {
$options = $fan->toArray()['options'];
return [
$fan->name,
$options['hwmon'],
$options['fan'],
$options['pwm'],
$options['start'],
$options['max'],
];
return $fan->toRow();
}
return [
......
......@@ -11,4 +11,6 @@ abstract class Fan extends Model
abstract public function setSpeed($percentage);
abstract public function stopFan();
abstract public function toRow(): array;
}
......@@ -2,8 +2,11 @@
namespace App\Model\Fan;
use App\Application;
use App\Model\Concerns\DetectsHwmon;
use App\Model\Fan;
use App\Service\Utils;
use Hugga\Console;
class HwmonFan extends Fan
{
......@@ -13,6 +16,8 @@ class HwmonFan extends Fan
const ENABLE_MANUAL = 1;
const ENABLE_MIN_AUTOMATIC = 2;
protected const UPDATE_INTERVAL = 1_600_000; // in microseconds
/** @var string */
protected $hwmon;
......@@ -72,12 +77,22 @@ class HwmonFan extends Fan
return $this;
}
public function getStartValue(): int
{
return $this->startValue;
}
public function setMaxValue(int $max): self
{
$this->maxValue = $max;
return $this;
}
public function getMaxValue(): int
{
return $this->maxValue;
}
public function setPwm(int $value)
{
if ($value < 0 or $value > 255) {
......@@ -87,6 +102,53 @@ class HwmonFan extends Fan
file_put_contents($this->basePath . $this->pwm, $value);
}
public function getPwm(): int
{
$pwm = file_get_contents($this->basePath . $this->pwm);
return (int)$pwm;
}
public function setPwmAndWait(int $value): int
{
return $this->getPwm() > $value ? $this->decreasePwm($value) : $this->increasePwm($value);
}
protected function decreasePwm(int $value): int
{
$this->setPwm($value);
usleep(self::UPDATE_INTERVAL);
if ($this->getPwm() !== $value) {
throw new \LogicException('This fan is controlled by an external application');
}
$currentSpeed = $this->getCurrentSpeed();
do {
$lastSpeed = $currentSpeed;
usleep(self::UPDATE_INTERVAL);
$currentSpeed = $this->getCurrentSpeed();
} while ($currentSpeed < $lastSpeed);
return $this->getCurrentSpeed();
}
protected function increasePwm(int $value): int
{
$this->setPwm($value);
usleep(self::UPDATE_INTERVAL);
if ($this->getPwm() !== $value) {
throw new \LogicException('This fan is controlled by an external application');
}
$currentSpeed = $this->getCurrentSpeed();
do {
$lastSpeed = $currentSpeed;
usleep(self::UPDATE_INTERVAL);
$currentSpeed = $this->getCurrentSpeed();
} while ($currentSpeed > $lastSpeed);
return $this->getCurrentSpeed();
}
public function setSpeed($percentage)
{
$this->setPwm($this->startValue + ($this->maxValue - $this->startValue) * $percentage / 100);
......@@ -112,6 +174,18 @@ class HwmonFan extends Fan
];
}
public function toRow(): array
{
return [
$this->name,
$this->hwmon,
$this->fan,
$this->pwm,
$this->startValue,
$this->maxValue,
];
}
protected function updateCurrentState()
{
$this->currentState = [
......
<?php
namespace App\Service;
class SystemCtl
{
public static function isActive(string $service): bool
{
return trim(exec('systemctl is-active ' . escapeshellarg($service))) === 'active';
}
public static function stop(string $service): bool
{
$status = trim(exec('systemctl is-active ' . escapeshellarg($service)));
if ($status === 'active') {
exec('systemctl stop ' . escapeshellarg($service));
}
return trim(exec('systemctl is-active ' . escapeshellarg($service))) === 'inactive';
}
public static function start(string $service): bool
{
$status = trim(exec('systemctl is-active ' . escapeshellarg($service)));
if ($status === 'inactive') {
exec('systemctl start ' . escapeshellarg($service));
}
return trim(exec('systemctl is-active ' . escapeshellarg($service))) === 'active';
}
}
<?php
namespace App\Service;
use App\Application;
use App\Model\Fan\HwmonFan;
use Hugga\Console;
class Utils
{
public static function waitForStableRpm(HwmonFan $fan): int
{
static $waitTime = 600_000; // in micro seconds
static $count = 4;
$readings = [];
// get the first readings
for ($i = 0; $i < $count; $i++) {
$readings[] = $fan->getCurrentSpeed();
usleep($waitTime);
}
$min = min($readings);
$max = max($readings);
$average = array_sum($readings) / $count;
$variance = $average > 0 ? ($max - $min) / $average * 100 : 0;
// wait till variance is less than 2
while (array_sum($readings) > 0 && // fan has not stopped
$variance > 2
) {
Application::console()->line(sprintf(
'min: %d rpm, max: %d rpm, avg: %d rpm, variance: %f%%',
$min,
$max,
$average,
$variance
), Console::WEIGHT_DEBUG);
array_shift($readings);
$readings[] = $fan->getCurrentSpeed();
$min = min($readings);
$max = max($readings);
$average = array_sum($readings) / $count;
$variance = $average > 0 ? ($max - $min) / $average * 100 : 0;
usleep($waitTime);
}
return $average;
}
}
<?php
namespace App\Subroutines\Fan;
use App\Application;
use App\Concerns\InteractiveConsole;
use App\Model\Fan\HwmonFan;
use App\Service\Utils;
use Hugga\Console;
use Hugga\Input\Question\Choice;
use Hugga\Input\Question\Confirmation;
class EditFan
{
use InteractiveConsole;
/** @var Application */
protected $app;
public function __construct(Application $app, Console $console)
{
$this->app = $app;
$this->console = $console;
}
public function main(HwmonFan $fan)
{
do {
$this->table([$fan->toRow()], ['Name', 'hwmon', 'Fan', 'PWM', 'start', 'max']);
$answer = $this->ask(new Choice([
'rename fan',
'adjust start value',
'define limit',
'done / exit',
], 'What to change?', 'done / exit'));
switch ($answer) {
case 'adjust start value':
$this->warn('Going to stop the fan. If you experience overheating shut stop the application' .
' immediately by hitting ctrl+c');
$speed = $fan->setPwmAndWait(0);
$this->line('at 0 pwm the fan runs at ' . $speed . ' rpm');
$startValue = $this->ask('What value to try?', $fan->getStartValue());
do {
$speed = $fan->setPwmAndWait($startValue);
$this->line('at ' . $startValue . ' pwm the fan runs at ' . $speed . ' rpm');
$answer = $this->ask('Try another value or keep this value?', 'keep');
} while ($answer !== 'keep' && $startValue = $answer);
$fan->resetState();
$fan->setStartValue($startValue);
break;
case 'define limit':
$this->info('Accelerating fan to current limit...');
$maxValue = $fan->getMaxValue();
$startValue = $fan->getStartValue();
do {
$speed = $fan->setPwmAndWait($maxValue);
$this->line('at ' . $maxValue . ' pwm the fan runs at ' . $speed . ' rpm');
$answer = $this->ask(
'Try another value (between ' . $startValue . ' and 255) or keep this value?',
'keep'
);
} while ($answer !== 'keep' && $maxValue = $answer);
$fan->resetState();
$fan->setMaxValue($maxValue);
break;
case 'rename fan':
$name = $this->ask('New name: ', $fan->name);
$fan->name = $name;
break;
case 'done / exit':
return 0;
}
} while (true);
}
}
......@@ -6,6 +6,7 @@ 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;
......@@ -17,9 +18,6 @@ class InitializeConfig
/** @var Application */
protected $app;
/**
* @param Application $app
*/
public function __construct(Application $app, Console $console)
{
$this->app = $app;
......@@ -38,47 +36,24 @@ class InitializeConfig
$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([
'name fan',
'adjust start value',
'define limit',
'edit fan',
'add fan',
'skip fan',
], 'What next?', 'name fan'));
'skip fan (don\'t include fan in config)',
], 'What next?', 'edit fan'));
switch ($answer) {
case 'adjust start value':
do {
$fan->setPwm(0);
$startValue = $this->ask('What value to try?', $start);
$fan->setPwm($startValue);
} while (!$this->ask(new Confirmation('Use that value?')));
$fan->resetState();
$fan->setStartValue($startValue);
break;
case 'define limit':
do {
$fan->setPwm(255);
$maxValue = $this->ask('What value to try?', 255);
$fan->setPwm($maxValue);
Utils::waitForStableRpm($fan);
$this->line('fan runs at ' . $fan->getCurrentSpeed() . ' rpm now');
} while (!$this->ask(new Confirmation('Use that value?')));
$fan->resetState();
$fan->setMaxValue($maxValue);
break;
case 'name fan':
$name = $this->ask('Name of the fan: ', $fan->name);
$fan->name = $name;
case 'edit fan':
$this->app->get(EditFan::class)->main($fan);
break;
case 'add fan':
$fanConfig->addFan($fan);
break 2;
case 'skip fan':
break 2;
default:
$this->warn('not implemented');
break;
}
} while (true);
}
......@@ -181,8 +156,7 @@ class InitializeConfig
protected function testPwm(HwmonFan $fan): array
{
$fan->setPwm(255);
Utils::waitForStableRpm($fan);
$fan->setPwmAndWait(255);
$fullSpeed = $fan->getCurrentSpeed();
if ($fullSpeed === 0) {
......@@ -190,8 +164,7 @@ class InitializeConfig
return [0, 0];
}
$fan->setPwm(0);
Utils::waitForStableRpm($fan);
$fan->setPwmAndWait(0);
$stopSpeed = $fan->getCurrentSpeed();
$fan->resetState();
......@@ -201,14 +174,12 @@ class InitializeConfig
protected function detectStartValue(HwmonFan $fan): int
{
$this->info('Detecting start value for ' . $fan->name);
$fan->setPwm(0);
Utils::waitForStableRpm($fan);
$fan->setPwmAndWait(0);
$minSpeed = $fan->getCurrentSpeed() * 1.1;
$pwm = 0;
do {
$fan->setPwm($pwm += 5);
Utils::waitForStableRpm($fan);
$fan->setPwmAndWait($pwm += 5);
$currentSpeed = $fan->getCurrentSpeed();
$this->line(sprintf('%d rpm with pwm value %d', $currentSpeed, $pwm));
} while ($minSpeed >= $currentSpeed);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment