added governor control code
This commit is contained in:
@@ -4,7 +4,7 @@ from pathlib import Path
|
||||
from logger import getLogger
|
||||
from mapping import trigger_mapping
|
||||
from config import Config
|
||||
from governor import Governor
|
||||
from governor import GovernorControl
|
||||
from schedule import TriggerScheduler
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ async def main() -> None:
|
||||
if config.testmode:
|
||||
log.warning("starting in testmode, cpu adjustments have been disabled")
|
||||
|
||||
governor = Governor(config)
|
||||
governor = GovernorControl(config)
|
||||
scheduler = TriggerScheduler(config)
|
||||
for triggertuple in config.triggertuples:
|
||||
_trigger = trigger_mapping[triggertuple.name](config)
|
||||
|
||||
@@ -31,4 +31,5 @@
|
||||
</customConfig>
|
||||
</trigger>
|
||||
</triggers>
|
||||
<powersaveDelayInMinutes>5</powersaveDelayInMinutes>
|
||||
</cpuGovernorAutoAdjust>
|
||||
|
||||
+29
-8
@@ -2,12 +2,26 @@ from app_class import AppClass
|
||||
from config import Config
|
||||
from cpufreq import cpuFreq, cpufreq # type: ignore
|
||||
from functools import cached_property
|
||||
from mapping import governor_priority_mapping
|
||||
from exceptions import GovernorNotFound
|
||||
from typing import Optional
|
||||
from typing import Optional, NamedTuple
|
||||
|
||||
|
||||
class Governor(NamedTuple):
|
||||
name: str
|
||||
priority: int
|
||||
|
||||
|
||||
_governor_list: list[Governor] = [
|
||||
Governor("performance", 0),
|
||||
Governor("schedutil", 1),
|
||||
Governor("ondemand", 2),
|
||||
Governor("conservative", 3),
|
||||
Governor("userspace", 4),
|
||||
Governor("powersave", 5)
|
||||
]
|
||||
|
||||
|
||||
class Governor(AppClass):
|
||||
class GovernorControl(AppClass):
|
||||
def __init__(self, _config: Config) -> None:
|
||||
super().__init__(_config)
|
||||
self.log.info("current governor is: %s", self.current_governor)
|
||||
@@ -21,20 +35,27 @@ class Governor(AppClass):
|
||||
return None
|
||||
|
||||
@property
|
||||
def current_governor(self) -> Optional[str]:
|
||||
def current_governor(self) -> Optional[Governor]:
|
||||
if self._cpufreq is None:
|
||||
return None
|
||||
_governor = set(self._cpufreq.get_governors().values())
|
||||
_governor_to_return = list(_governor)[0]
|
||||
_governor_to_return = self._establish_governor(list(_governor)[0])
|
||||
if len(_governor) == 0:
|
||||
raise GovernorNotFound("Unable to retrieve current governor")
|
||||
if len(_governor) > 1:
|
||||
self.log.error("multiple governors have been set, which is not expected.")
|
||||
# returning governor with highest priority based on performance
|
||||
for gov in _governor:
|
||||
if governor_priority_mapping[gov] < governor_priority_mapping[_governor_to_return]:
|
||||
_governor_to_return == gov
|
||||
return _governor_to_return
|
||||
t_gov = self._establish_governor(gov)
|
||||
if t_gov.priority < _governor_to_return.priority:
|
||||
_governor_to_return == t_gov
|
||||
return _governor_to_return
|
||||
|
||||
def _establish_governor(self, governor_name: str) -> Governor:
|
||||
governor = next((g for g in _governor_list if g.name == governor_name), None)
|
||||
if governor is None:
|
||||
raise RuntimeError("could not establish governor")
|
||||
return governor
|
||||
|
||||
def set_governor(self, governor_name: str) -> None:
|
||||
self.log.debug(
|
||||
|
||||
-11
@@ -7,14 +7,3 @@ _trigger_mapping = {
|
||||
'roon': RoonTrigger
|
||||
}
|
||||
trigger_mapping = MappingProxyType(_trigger_mapping)
|
||||
|
||||
_governor_priority_mapping = {
|
||||
'performance': 0,
|
||||
'schedutil': 1,
|
||||
'ondemand': 2,
|
||||
'conservative': 3,
|
||||
'userspace': 4,
|
||||
'performance': 5
|
||||
}
|
||||
|
||||
governor_priority_mapping = MappingProxyType(_governor_priority_mapping)
|
||||
|
||||
+21
-4
@@ -1,29 +1,44 @@
|
||||
from app_class import AppClass
|
||||
from config import Config
|
||||
from trigger import Trigger
|
||||
from governor import GovernorControl, Governor, _governor_list
|
||||
import asyncio
|
||||
from asyncio import Task
|
||||
from functools import cached_property
|
||||
|
||||
|
||||
class TriggerScheduler(AppClass):
|
||||
def __init__(self, _config: Config) -> None:
|
||||
super().__init__(_config)
|
||||
self.tasks: list[Task] = []
|
||||
self.running_triggers: list[Trigger] = []
|
||||
|
||||
@cached_property
|
||||
def governor_control(self) -> GovernorControl:
|
||||
return GovernorControl(self._config)
|
||||
|
||||
def establish_preferred_governor(self) -> str:
|
||||
preferred_governor: Governor = [gov for gov in _governor_list if gov.name == 'powersave'].pop()
|
||||
for trigger in self.running_triggers:
|
||||
self.log.debug('trigger %s, state: %s, preferred governor: %s', trigger.name, trigger.trigger_state, trigger.governor.name)
|
||||
if trigger.active:
|
||||
if trigger.governor.priority < preferred_governor.priority:
|
||||
preferred_governor = trigger.governor
|
||||
self.log.debug('preferred governor: %s', preferred_governor.name)
|
||||
return preferred_governor.name
|
||||
|
||||
async def callback_trigger(self, _trigger: Trigger) -> None:
|
||||
"""Run a callback_trigger with a specific name and check its status at a given interval."""
|
||||
await _trigger.async_run()
|
||||
while True:
|
||||
trigger_state = "active" if _trigger.active else "not active"
|
||||
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, trigger_state)
|
||||
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, _trigger.trigger_state)
|
||||
await asyncio.sleep(_trigger.config.interval_in_seconds)
|
||||
|
||||
async def run_once_trigger(self, _trigger: Trigger) -> None:
|
||||
"""Run a trigger with a specific name at a given interval."""
|
||||
while True:
|
||||
_trigger.run()
|
||||
trigger_state = "active" if _trigger.active else "not active"
|
||||
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, trigger_state)
|
||||
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, _trigger.trigger_state)
|
||||
await asyncio.sleep(_trigger.config.interval_in_seconds)
|
||||
|
||||
def start_trigger(self, _trigger: Trigger) -> None:
|
||||
@@ -32,6 +47,7 @@ class TriggerScheduler(AppClass):
|
||||
task = asyncio.create_task(self.callback_trigger(_trigger))
|
||||
else:
|
||||
task = asyncio.create_task(self.run_once_trigger(_trigger))
|
||||
self.running_triggers.append(_trigger)
|
||||
self.tasks.append(task)
|
||||
|
||||
async def stop_triggers(self) -> None:
|
||||
@@ -44,6 +60,7 @@ class TriggerScheduler(AppClass):
|
||||
"""Run the scheduler and keep it alive until stopped."""
|
||||
try:
|
||||
while True:
|
||||
self.establish_preferred_governor()
|
||||
await asyncio.sleep(1) # Keep the main function alive
|
||||
except asyncio.exceptions.CancelledError:
|
||||
self.log.info("Exiting, stopping all triggers")
|
||||
|
||||
+16
-1
@@ -1,12 +1,21 @@
|
||||
from app_class import AppClass
|
||||
from config import Config, TriggerTuple
|
||||
from exceptions import MissingConfig, TriggerImportError
|
||||
from governor import Governor, _governor_list
|
||||
from functools import cached_property
|
||||
|
||||
|
||||
class Trigger(AppClass):
|
||||
def __init__(self, _config: Config) -> None:
|
||||
super().__init__(_config)
|
||||
self.active: bool = False
|
||||
|
||||
def __hash__(self):
|
||||
return super().__hash__(hash(self.name))
|
||||
|
||||
@property
|
||||
def trigger_state(self) -> str:
|
||||
return "active" if self.active else "not active"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -24,6 +33,13 @@ class Trigger(AppClass):
|
||||
raise MissingConfig(f"Trigger {self.name} hasn't been properly configured")
|
||||
return _config
|
||||
|
||||
@cached_property
|
||||
def governor(self) -> Governor:
|
||||
_governor = next((gov for gov in _governor_list if self.config.governor == gov.name), None)
|
||||
if _governor is None:
|
||||
raise ValueError("unknown governor: %s", _governor)
|
||||
return _governor
|
||||
|
||||
def run(self) -> None:
|
||||
# this is the main function of the trigger class and has to stay active by using a while True loop
|
||||
|
||||
@@ -39,4 +55,3 @@ class Trigger(AppClass):
|
||||
raise TriggerImportError(
|
||||
"the Trigger class can't used directly, but must be inherited in a trigger specific class"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user