Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
67d448e0a0
13 changed files with 353 additions and 245 deletions
|
|
@ -7,7 +7,6 @@ from . import log
|
||||||
from . import main
|
from . import main
|
||||||
from . import music
|
from . import music
|
||||||
from . import net
|
from . import net
|
||||||
from . import python
|
|
||||||
from . import repairs
|
from . import repairs
|
||||||
from . import setup
|
from . import setup
|
||||||
from . import sources
|
from . import sources
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,13 @@ BADBLOCKS_RESULTS_REGEX = re.compile(
|
||||||
r'^(Checking for bad blocks .read-only test.: ).*\x08+(done|\s+).*?(\x08+)?'
|
r'^(Checking for bad blocks .read-only test.: ).*\x08+(done|\s+).*?(\x08+)?'
|
||||||
)
|
)
|
||||||
BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE)
|
BADBLOCKS_SKIP_REGEX = re.compile(r'^(Checking|\[)', re.IGNORECASE)
|
||||||
CPU_CRITICAL_TEMP = 100
|
CPU_TEMPS = {
|
||||||
CPU_FAILURE_TEMP = 90
|
'Cooling Delta': 25,
|
||||||
|
'Cooling Low Cutoff': 50,
|
||||||
|
'Critical': 100,
|
||||||
|
'Idle Delta': 25,
|
||||||
|
'Idle High': 80,
|
||||||
|
}
|
||||||
CPU_TEST_MINUTES = 7
|
CPU_TEST_MINUTES = 7
|
||||||
IO_GRAPH_WIDTH = 40
|
IO_GRAPH_WIDTH = 40
|
||||||
IO_ALT_TEST_SIZE_FACTOR = 0.01
|
IO_ALT_TEST_SIZE_FACTOR = 0.01
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
"""WizardKit: Config - Python"""
|
|
||||||
# vim: sts=2 sw=2 ts=2
|
|
||||||
|
|
||||||
from sys import version_info
|
|
||||||
|
|
||||||
DATACLASS_DECORATOR_KWARGS = {}
|
|
||||||
if version_info.major >= 3 and version_info.minor >= 10:
|
|
||||||
DATACLASS_DECORATOR_KWARGS['slots'] = True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print("This file is not meant to be called directly.")
|
|
||||||
|
|
||||||
# vim: sts=2 sw=2 ts=2
|
|
||||||
|
|
@ -72,6 +72,10 @@ REG_WINDOWS_EXPLORER = {
|
||||||
('PublishUserActivities', 0, 'DWORD'),
|
('PublishUserActivities', 0, 'DWORD'),
|
||||||
('UploadUserActivities', 0, 'DWORD'),
|
('UploadUserActivities', 0, 'DWORD'),
|
||||||
),
|
),
|
||||||
|
# Disable floating Bing search widget
|
||||||
|
r'Software\Policies\Microsoft\Edge': (
|
||||||
|
('WebWidgetAllowed', 0, 'DWORD'),
|
||||||
|
),
|
||||||
# Disable Edge first run screen
|
# Disable Edge first run screen
|
||||||
r'Software\Policies\Microsoft\MicrosoftEdge\Main': (
|
r'Software\Policies\Microsoft\MicrosoftEdge\Main': (
|
||||||
('PreventFirstRunPage', 1, 'DWORD'),
|
('PreventFirstRunPage', 1, 'DWORD'),
|
||||||
|
|
@ -135,6 +139,7 @@ REG_OPEN_SHELL_SETTINGS = {
|
||||||
('ShowedStyle2', 1, 'DWORD'),
|
('ShowedStyle2', 1, 'DWORD'),
|
||||||
),
|
),
|
||||||
r'Software\OpenShell\StartMenu\Settings': (
|
r'Software\OpenShell\StartMenu\Settings': (
|
||||||
|
('HighlightNew', 0, 'DWORD'),
|
||||||
('MenuStyle', 'Win7', 'SZ'),
|
('MenuStyle', 'Win7', 'SZ'),
|
||||||
('RecentPrograms', 'Recent', 'SZ'),
|
('RecentPrograms', 'Recent', 'SZ'),
|
||||||
('SkinW7', 'Fluent-Metro', 'SZ'),
|
('SkinW7', 'Fluent-Metro', 'SZ'),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from io import BufferedReader, TextIOWrapper
|
from io import IOBase
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Any, Callable, Iterable
|
from typing import Any, Callable, Iterable
|
||||||
|
|
@ -28,11 +28,11 @@ class NonBlockingStreamReader():
|
||||||
## https://gist.github.com/EyalAr/7915597
|
## https://gist.github.com/EyalAr/7915597
|
||||||
## https://stackoverflow.com/a/4896288
|
## https://stackoverflow.com/a/4896288
|
||||||
|
|
||||||
def __init__(self, stream: BufferedReader | TextIOWrapper):
|
def __init__(self, stream: IOBase):
|
||||||
self.stream: BufferedReader | TextIOWrapper = stream
|
self.stream: IOBase = stream
|
||||||
self.queue: Queue = Queue()
|
self.queue: Queue = Queue()
|
||||||
|
|
||||||
def populate_queue(stream: BufferedReader | TextIOWrapper, queue: Queue) -> None:
|
def populate_queue(stream: IOBase, queue: Queue) -> None:
|
||||||
"""Collect lines from stream and put them in queue."""
|
"""Collect lines from stream and put them in queue."""
|
||||||
while not stream.closed:
|
while not stream.closed:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import subprocess
|
||||||
from typing import TextIO
|
from typing import TextIO
|
||||||
|
|
||||||
from wk import exe
|
from wk import exe
|
||||||
from wk.cfg.hw import CPU_FAILURE_TEMP
|
from wk.cfg.hw import CPU_TEMPS
|
||||||
from wk.os.mac import set_fans as macos_set_fans
|
from wk.os.mac import set_fans as macos_set_fans
|
||||||
from wk.std import PLATFORM
|
from wk.std import PLATFORM
|
||||||
from wk.ui import ansi
|
from wk.ui import ansi
|
||||||
|
|
@ -20,32 +20,71 @@ SysbenchType = tuple[subprocess.Popen, TextIO]
|
||||||
|
|
||||||
|
|
||||||
# Functions
|
# Functions
|
||||||
def check_cooling_results(test_obj, sensors, run_sysbench=False) -> None:
|
def check_cooling_results(sensors, test_object) -> None:
|
||||||
"""Check cooling results and update test_obj."""
|
"""Check cooling result via sensor data."""
|
||||||
max_temp = sensors.cpu_max_temp()
|
idle_temp = sensors.get_cpu_temp('Idle')
|
||||||
temp_labels = ['Idle', 'Max', 'Cooldown']
|
cooldown_temp = sensors.get_cpu_temp('Cooldown')
|
||||||
if run_sysbench:
|
max_temp = sensors.get_cpu_temp('Max')
|
||||||
temp_labels.append('Sysbench')
|
test_object.report.append(ansi.color_string('Temps', 'BLUE'))
|
||||||
|
|
||||||
# Check temps
|
# Check temps
|
||||||
if not max_temp:
|
if max_temp > CPU_TEMPS['Critical']:
|
||||||
test_obj.set_status('Unknown')
|
test_object.failed = True
|
||||||
elif max_temp >= CPU_FAILURE_TEMP:
|
test_object.set_status('Failed')
|
||||||
test_obj.failed = True
|
test_object.report.extend([
|
||||||
test_obj.set_status('Failed')
|
ansi.color_string(
|
||||||
elif 'Aborted' not in test_obj.status:
|
f' WARNING: Critical CPU temp of {CPU_TEMPS["Critical"]} exceeded.',
|
||||||
test_obj.passed = True
|
'RED',
|
||||||
test_obj.set_status('Passed')
|
),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
elif idle_temp >= CPU_TEMPS['Idle High']:
|
||||||
|
test_object.failed = True
|
||||||
|
test_object.set_status('Failed')
|
||||||
|
test_object.report.extend([
|
||||||
|
ansi.color_string(
|
||||||
|
f' WARNING: Max idle temp of {CPU_TEMPS["Idle High"]} exceeded.',
|
||||||
|
'YELLOW',
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
elif (
|
||||||
|
cooldown_temp <= CPU_TEMPS['Cooling Low Cutoff']
|
||||||
|
or max_temp - cooldown_temp >= CPU_TEMPS['Cooling Delta']
|
||||||
|
):
|
||||||
|
test_object.passed = True
|
||||||
|
test_object.set_status('Passed')
|
||||||
|
else:
|
||||||
|
test_object.passed = False
|
||||||
|
test_object.set_status('Unknown')
|
||||||
|
if cooldown_temp - idle_temp >= CPU_TEMPS['Idle Delta']:
|
||||||
|
test_object.report.extend([
|
||||||
|
ansi.color_string(
|
||||||
|
f' WARNING: Cooldown temp at least {CPU_TEMPS["Idle Delta"]}° over idle.',
|
||||||
|
'YELLOW',
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
])
|
||||||
|
|
||||||
# Add temps to report
|
# Build report
|
||||||
for line in sensors.generate_report(*temp_labels, only_cpu=True):
|
report_labels = ['Idle']
|
||||||
test_obj.report.append(f' {line}')
|
if 'Sysbench' in sensors.temp_labels:
|
||||||
|
report_labels.extend(['Sysbench', 'Cooldown'])
|
||||||
|
if 'Prime95' in sensors.temp_labels:
|
||||||
|
report_labels.append('Prime95')
|
||||||
|
if 'Cooldown' not in report_labels:
|
||||||
|
report_labels.append('Cooldown')
|
||||||
|
if len(sensors.temp_labels.intersection(['Prime95', 'Sysbench'])) < 1:
|
||||||
|
# Include overall max temp if needed
|
||||||
|
report_labels.append('Max')
|
||||||
|
for line in sensors.generate_report(*report_labels, only_cpu=True):
|
||||||
|
test_object.report.append(f' {line}')
|
||||||
|
|
||||||
|
|
||||||
def check_mprime_results(test_obj, working_dir) -> None:
|
def check_mprime_results(test_obj, working_dir) -> None:
|
||||||
"""Check mprime log files and update test_obj."""
|
"""Check mprime log files and update test_obj."""
|
||||||
passing_lines = {}
|
passing_lines = set()
|
||||||
warning_lines = {}
|
warning_lines = set()
|
||||||
|
|
||||||
def _read_file(log_name) -> list[str]:
|
def _read_file(log_name) -> list[str]:
|
||||||
"""Read file and split into lines, returns list."""
|
"""Read file and split into lines, returns list."""
|
||||||
|
|
@ -63,7 +102,7 @@ def check_mprime_results(test_obj, working_dir) -> None:
|
||||||
for line in _read_file('results.txt'):
|
for line in _read_file('results.txt'):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if re.search(r'(error|fail)', line, re.IGNORECASE):
|
if re.search(r'(error|fail)', line, re.IGNORECASE):
|
||||||
warning_lines[line] = None
|
warning_lines.add(line)
|
||||||
|
|
||||||
# prime.log (check if passed)
|
# prime.log (check if passed)
|
||||||
for line in _read_file('prime.log'):
|
for line in _read_file('prime.log'):
|
||||||
|
|
@ -73,10 +112,10 @@ def check_mprime_results(test_obj, working_dir) -> None:
|
||||||
if match:
|
if match:
|
||||||
if int(match.group(2)) + int(match.group(3)) > 0:
|
if int(match.group(2)) + int(match.group(3)) > 0:
|
||||||
# Errors and/or warnings encountered
|
# Errors and/or warnings encountered
|
||||||
warning_lines[match.group(1).capitalize()] = None
|
warning_lines.add(match.group(1).capitalize())
|
||||||
else:
|
else:
|
||||||
# No errors/warnings
|
# No errors/warnings
|
||||||
passing_lines[match.group(1).capitalize()] = None
|
passing_lines.add(match.group(1).capitalize())
|
||||||
|
|
||||||
# Update status
|
# Update status
|
||||||
if warning_lines:
|
if warning_lines:
|
||||||
|
|
@ -112,7 +151,9 @@ def start_mprime(working_dir, log_path) -> subprocess.Popen:
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
)
|
)
|
||||||
proc_mprime.stdout.close() # type: ignore[reportOptionalMemberAccess]
|
proc_mprime.stdout.close() # type: ignore[reportOptionalMemberAccess]
|
||||||
save_nbsr = exe.NonBlockingStreamReader(proc_grep.stdout)
|
save_nbsr = exe.NonBlockingStreamReader(
|
||||||
|
proc_grep.stdout, # type: ignore[reportGeneralTypeIssues]
|
||||||
|
)
|
||||||
exe.start_thread(
|
exe.start_thread(
|
||||||
save_nbsr.save_to_file,
|
save_nbsr.save_to_file,
|
||||||
args=(proc_grep, log_path),
|
args=(proc_grep, log_path),
|
||||||
|
|
@ -122,35 +163,6 @@ def start_mprime(working_dir, log_path) -> subprocess.Popen:
|
||||||
return proc_mprime
|
return proc_mprime
|
||||||
|
|
||||||
|
|
||||||
def start_sysbench(sensors, sensors_out, log_path) -> SysbenchType:
|
|
||||||
"""Start sysbench, returns tuple with Popen object and file handle."""
|
|
||||||
set_apple_fan_speed('max')
|
|
||||||
sysbench_cmd = [
|
|
||||||
'sysbench',
|
|
||||||
f'--threads={exe.psutil.cpu_count()}',
|
|
||||||
'--cpu-max-prime=1000000000',
|
|
||||||
'cpu',
|
|
||||||
'run',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Restart background monitor for Sysbench
|
|
||||||
sensors.stop_background_monitor()
|
|
||||||
sensors.start_background_monitor(
|
|
||||||
sensors_out,
|
|
||||||
alt_max='Sysbench',
|
|
||||||
thermal_action=('killall', 'sysbench', '-INT'),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Start sysbench
|
|
||||||
filehandle_sysbench = open(
|
|
||||||
log_path, 'a', encoding='utf-8',
|
|
||||||
)
|
|
||||||
proc_sysbench = exe.popen_program(sysbench_cmd, stdout=filehandle_sysbench)
|
|
||||||
|
|
||||||
# Done
|
|
||||||
return (proc_sysbench, filehandle_sysbench)
|
|
||||||
|
|
||||||
|
|
||||||
def set_apple_fan_speed(speed) -> None:
|
def set_apple_fan_speed(speed) -> None:
|
||||||
"""Set Apple fan speed."""
|
"""Set Apple fan speed."""
|
||||||
cmd = None
|
cmd = None
|
||||||
|
|
@ -174,6 +186,27 @@ def set_apple_fan_speed(speed) -> None:
|
||||||
exe.run_program(cmd, check=False)
|
exe.run_program(cmd, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def start_sysbench(log_path) -> SysbenchType:
|
||||||
|
"""Start sysbench, returns tuple with Popen object and file handle."""
|
||||||
|
set_apple_fan_speed('max')
|
||||||
|
cmd = [
|
||||||
|
'sysbench',
|
||||||
|
f'--threads={exe.psutil.cpu_count()}',
|
||||||
|
'--cpu-max-prime=1000000000',
|
||||||
|
'cpu',
|
||||||
|
'run',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Start sysbench
|
||||||
|
filehandle = open(
|
||||||
|
log_path, 'a', encoding='utf-8',
|
||||||
|
)
|
||||||
|
proc = exe.popen_program(cmd, stdout=filehandle)
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return (proc, filehandle)
|
||||||
|
|
||||||
|
|
||||||
def stop_mprime(proc_mprime) -> None:
|
def stop_mprime(proc_mprime) -> None:
|
||||||
"""Stop mprime gracefully, then forcefully as needed."""
|
"""Stop mprime gracefully, then forcefully as needed."""
|
||||||
proc_mprime.terminate()
|
proc_mprime.terminate()
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,9 @@ TEST_GROUPS = {
|
||||||
# Also used to build the menu options
|
# Also used to build the menu options
|
||||||
## NOTE: This needs to be above MENU_SETS
|
## NOTE: This needs to be above MENU_SETS
|
||||||
'System Info': 'post_system_info',
|
'System Info': 'post_system_info',
|
||||||
'CPU & Cooling': 'cpu_stress_tests',
|
'CPU (Sysbench)': 'cpu_test_sysbench',
|
||||||
|
'CPU (Prime95)': 'cpu_test_mprime',
|
||||||
|
'CPU (Cooling)': 'cpu_test_cooling',
|
||||||
'Disk Attributes': 'disk_attribute_check',
|
'Disk Attributes': 'disk_attribute_check',
|
||||||
'Disk Self-Test': 'disk_self_test',
|
'Disk Self-Test': 'disk_self_test',
|
||||||
'Disk Surface Scan': 'disk_surface_scan',
|
'Disk Surface Scan': 'disk_surface_scan',
|
||||||
|
|
@ -80,6 +82,7 @@ MENU_ACTIONS_SECRET = (
|
||||||
MENU_OPTIONS_QUICK = ('Disk Attributes',)
|
MENU_OPTIONS_QUICK = ('Disk Attributes',)
|
||||||
MENU_SETS = {
|
MENU_SETS = {
|
||||||
'Full Diagnostic': (*TEST_GROUPS,),
|
'Full Diagnostic': (*TEST_GROUPS,),
|
||||||
|
'CPU Diagnostic': (*[group for group in TEST_GROUPS if group.startswith('CPU')],),
|
||||||
'Disk Diagnostic': (
|
'Disk Diagnostic': (
|
||||||
'Disk Attributes',
|
'Disk Attributes',
|
||||||
'Disk Self-Test',
|
'Disk Self-Test',
|
||||||
|
|
@ -101,11 +104,11 @@ PLATFORM = std.PLATFORM
|
||||||
class State():
|
class State():
|
||||||
"""Object for tracking hardware diagnostic data."""
|
"""Object for tracking hardware diagnostic data."""
|
||||||
def __init__(self, test_mode=False):
|
def __init__(self, test_mode=False):
|
||||||
self.cpu_max_temp = -1
|
|
||||||
self.disks: list[hw_disk.Disk] = []
|
self.disks: list[hw_disk.Disk] = []
|
||||||
self.log_dir: pathlib.Path | None = None
|
self.log_dir: pathlib.Path | None = None
|
||||||
self.ost = osticket.osTicket()
|
self.ost = osticket.osTicket()
|
||||||
self.progress_file: pathlib.Path | None = None
|
self.progress_file: pathlib.Path | None = None
|
||||||
|
self.sensors: hw_sensors.Sensors = hw_sensors.Sensors()
|
||||||
self.system: hw_system.System | None = None
|
self.system: hw_system.System | None = None
|
||||||
self.test_groups: list[TestGroup] = []
|
self.test_groups: list[TestGroup] = []
|
||||||
self.title_text: str = ansi.color_string('Hardware Diagnostics', 'GREEN')
|
self.title_text: str = ansi.color_string('Hardware Diagnostics', 'GREEN')
|
||||||
|
|
@ -143,6 +146,7 @@ class State():
|
||||||
|
|
||||||
# Reset objects
|
# Reset objects
|
||||||
self.disks.clear()
|
self.disks.clear()
|
||||||
|
self.sensors = hw_sensors.Sensors()
|
||||||
self.test_groups.clear()
|
self.test_groups.clear()
|
||||||
self.ui.remove_all_subtitle_panes()
|
self.ui.remove_all_subtitle_panes()
|
||||||
|
|
||||||
|
|
@ -194,20 +198,8 @@ class State():
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'CPU' in name:
|
if 'CPU' in name:
|
||||||
# Create two Test objects which will both be used by cpu_stress_tests
|
|
||||||
# NOTE: Prime95 should be added first
|
|
||||||
self.system.tests.append(
|
self.system.tests.append(
|
||||||
Test(dev=self.system, label='Prime95', name=name),
|
Test(dev=self.system, label=name[5:-1], name=name),
|
||||||
)
|
|
||||||
self.system.tests.append(
|
|
||||||
Test(dev=self.system, label='Cooling', name=name),
|
|
||||||
)
|
|
||||||
self.test_groups.append(
|
|
||||||
TestGroup(
|
|
||||||
name=name,
|
|
||||||
function=globals()[TEST_GROUPS[name]],
|
|
||||||
test_objects=self.system.tests,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'Disk' in name:
|
if 'Disk' in name:
|
||||||
|
|
@ -220,6 +212,17 @@ class State():
|
||||||
test_group.test_objects.append(test_obj)
|
test_group.test_objects.append(test_obj)
|
||||||
self.test_groups.append(test_group)
|
self.test_groups.append(test_group)
|
||||||
|
|
||||||
|
# Group CPU tests
|
||||||
|
if self.system.tests:
|
||||||
|
self.test_groups.insert(
|
||||||
|
0,
|
||||||
|
TestGroup(
|
||||||
|
name='CPU & Cooling',
|
||||||
|
function=run_cpu_tests,
|
||||||
|
test_objects=self.system.tests,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def reset_layout(self) -> None:
|
def reset_layout(self) -> None:
|
||||||
"""Reset layout to avoid flickering."""
|
"""Reset layout to avoid flickering."""
|
||||||
self.ui.clear_current_pane_height()
|
self.ui.clear_current_pane_height()
|
||||||
|
|
@ -362,145 +365,200 @@ def build_menu(cli_mode=False, quick_mode=False) -> cli.Menu:
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
|
|
||||||
def cpu_stress_tests(state, test_objects, test_mode=False) -> None:
|
def cpu_tests_init(state) -> None:
|
||||||
"""CPU & cooling check using Prime95 and Sysbench."""
|
"""Initialize CPU tests."""
|
||||||
LOG.info('CPU Test (Prime95)')
|
|
||||||
aborted = False
|
|
||||||
prime_log = pathlib.Path(f'{state.log_dir}/prime.log')
|
|
||||||
run_sysbench = False
|
|
||||||
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||||
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
state.update_title_text(state.system.cpu_description)
|
||||||
if test_mode:
|
|
||||||
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
|
||||||
test_mprime_obj, test_cooling_obj = test_objects
|
|
||||||
|
|
||||||
# Bail early
|
# Start monitor
|
||||||
if test_cooling_obj.disabled or test_mprime_obj.disabled:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Prep
|
|
||||||
state.update_title_text(test_mprime_obj.dev.cpu_description)
|
|
||||||
test_cooling_obj.set_status('Working')
|
|
||||||
test_mprime_obj.set_status('Working')
|
|
||||||
|
|
||||||
# Start sensors monitor
|
|
||||||
sensors = hw_sensors.Sensors()
|
|
||||||
sensors.start_background_monitor(
|
|
||||||
sensors_out,
|
|
||||||
thermal_action=('killall', 'mprime', '-INT'),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create monitor and worker panes
|
|
||||||
state.update_progress_file()
|
|
||||||
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=prime_log)
|
|
||||||
if PLATFORM == 'Darwin':
|
if PLATFORM == 'Darwin':
|
||||||
state.ui.add_info_pane(
|
state.ui.add_info_pane(
|
||||||
percent=80, cmd='./hw-sensors', update_layout=False,
|
percent=80, cmd='./hw-sensors', update_layout=False,
|
||||||
)
|
)
|
||||||
elif PLATFORM == 'Linux':
|
elif PLATFORM == 'Linux':
|
||||||
state.ui.add_info_pane(
|
state.ui.add_info_pane(
|
||||||
percent=80, watch_file=sensors_out, update_layout=False,
|
percent=80,
|
||||||
|
watch_file=pathlib.Path(f'{state.log_dir}/sensors.out'),
|
||||||
|
update_layout=False,
|
||||||
)
|
)
|
||||||
|
state.sensors.start_background_monitor(sensors_out)
|
||||||
state.ui.set_current_pane_height(3)
|
state.ui.set_current_pane_height(3)
|
||||||
|
|
||||||
# Get idle temps
|
# Save idle temps
|
||||||
cli.print_standard('Saving idle temps...')
|
cli.print_standard('Saving idle temps...')
|
||||||
sensors.save_average_temps(temp_label='Idle', seconds=5)
|
state.sensors.save_average_temps(temp_label='Idle', seconds=5)
|
||||||
|
|
||||||
# Stress CPU
|
|
||||||
|
def cpu_tests_end(state) -> None:
|
||||||
|
"""End CPU tests."""
|
||||||
|
# Cleanup
|
||||||
|
state.sensors.stop_background_monitor()
|
||||||
|
state.ui.clear_current_pane_height()
|
||||||
|
state.ui.remove_all_info_panes()
|
||||||
|
state.ui.remove_all_worker_panes()
|
||||||
|
|
||||||
|
|
||||||
|
def cpu_test_cooling(state, test_object, test_mode=False) -> None:
|
||||||
|
"""CPU cooling test via sensor data assessment."""
|
||||||
|
LOG.info('CPU Test (Cooling)')
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if test_object.disabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
hw_cpu.check_cooling_results(state.sensors, test_object)
|
||||||
|
state.update_progress_file()
|
||||||
|
|
||||||
|
|
||||||
|
def cpu_test_mprime(state, test_object, test_mode=False) -> None:
|
||||||
|
"""CPU stress test using mprime."""
|
||||||
|
LOG.info('CPU Test (Prime95)')
|
||||||
|
aborted = False
|
||||||
|
log_path = pathlib.Path(f'{state.log_dir}/prime.log')
|
||||||
|
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||||
|
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||||
|
if test_mode:
|
||||||
|
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if test_object.disabled:
|
||||||
|
return
|
||||||
|
if state.sensors.cpu_reached_critical_temp():
|
||||||
|
test_object.set_status('Denied')
|
||||||
|
test_object.disabled = True
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prep
|
||||||
|
test_object.set_status('Working')
|
||||||
|
state.update_progress_file()
|
||||||
|
state.ui.clear_current_pane()
|
||||||
cli.print_info('Running stress test')
|
cli.print_info('Running stress test')
|
||||||
hw_cpu.set_apple_fan_speed('max')
|
|
||||||
proc_mprime = hw_cpu.start_mprime(state.log_dir, prime_log)
|
|
||||||
|
|
||||||
# Show countdown
|
|
||||||
print('')
|
print('')
|
||||||
|
|
||||||
|
# Start sensors monitor
|
||||||
|
state.sensors.stop_background_monitor()
|
||||||
|
state.sensors.start_background_monitor(
|
||||||
|
sensors_out,
|
||||||
|
alt_max='Prime95',
|
||||||
|
thermal_action=('killall', '-INT', 'mprime'),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run Prime95
|
||||||
|
hw_cpu.set_apple_fan_speed('max')
|
||||||
|
proc = hw_cpu.start_mprime(state.log_dir, log_path)
|
||||||
|
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=log_path)
|
||||||
try:
|
try:
|
||||||
print_countdown(proc=proc_mprime, seconds=test_minutes*60)
|
print_countdown(proc=proc, seconds=test_minutes*60)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
aborted = True
|
aborted = True
|
||||||
|
|
||||||
# Stop Prime95
|
# Stop Prime95
|
||||||
hw_cpu.stop_mprime(proc_mprime)
|
hw_cpu.stop_mprime(proc)
|
||||||
|
|
||||||
# Update progress if necessary
|
|
||||||
if sensors.cpu_reached_critical_temp() or aborted:
|
|
||||||
test_cooling_obj.set_status('Aborted')
|
|
||||||
test_mprime_obj.set_status('Aborted')
|
|
||||||
state.update_progress_file()
|
|
||||||
|
|
||||||
# Get cooldown temp
|
# Get cooldown temp
|
||||||
|
if 'Cooldown' in state.sensors.temp_labels:
|
||||||
|
# Give Prime95 time to save the results
|
||||||
|
std.sleep(1)
|
||||||
|
else:
|
||||||
|
# Save cooldown temp
|
||||||
state.ui.clear_current_pane()
|
state.ui.clear_current_pane()
|
||||||
cli.print_standard('Letting CPU cooldown...')
|
cli.print_standard('Letting CPU cooldown...')
|
||||||
std.sleep(5)
|
std.sleep(5)
|
||||||
cli.print_standard('Saving cooldown temps...')
|
cli.print_standard('Saving cooldown temps...')
|
||||||
sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||||
|
|
||||||
# Check Prime95 results
|
# Check Prime95 results
|
||||||
test_mprime_obj.report.append(ansi.color_string('Prime95', 'BLUE'))
|
test_object.report.append(ansi.color_string('Prime95', 'BLUE'))
|
||||||
hw_cpu.check_mprime_results(
|
hw_cpu.check_mprime_results(test_obj=test_object, working_dir=state.log_dir)
|
||||||
test_obj=test_mprime_obj, working_dir=state.log_dir,
|
|
||||||
|
# Update progress
|
||||||
|
if state.sensors.cpu_reached_critical_temp() or aborted:
|
||||||
|
test_object.set_status('Aborted')
|
||||||
|
state.update_progress_file()
|
||||||
|
|
||||||
|
# Done
|
||||||
|
state.ui.remove_all_worker_panes()
|
||||||
|
if aborted:
|
||||||
|
cpu_tests_end(state)
|
||||||
|
raise std.GenericAbort('Aborted')
|
||||||
|
|
||||||
|
|
||||||
|
def cpu_test_sysbench(state, test_object, test_mode=False) -> None:
|
||||||
|
"""CPU stress test using Sysbench."""
|
||||||
|
LOG.info('CPU Test (Sysbench)')
|
||||||
|
aborted = False
|
||||||
|
log_path = pathlib.Path(f'{state.log_dir}/sysbench.log')
|
||||||
|
sensors_out = pathlib.Path(f'{state.log_dir}/sensors.out')
|
||||||
|
test_minutes = cfg.hw.CPU_TEST_MINUTES
|
||||||
|
if test_mode:
|
||||||
|
test_minutes = cfg.hw.TEST_MODE_CPU_LIMIT
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if test_object.disabled:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Prep
|
||||||
|
test_object.set_status('Working')
|
||||||
|
state.update_progress_file()
|
||||||
|
state.ui.clear_current_pane()
|
||||||
|
cli.print_info('Running stress test')
|
||||||
|
print('')
|
||||||
|
|
||||||
|
# Start sensors monitor
|
||||||
|
state.sensors.stop_background_monitor()
|
||||||
|
state.sensors.start_background_monitor(
|
||||||
|
sensors_out,
|
||||||
|
alt_max='Sysbench',
|
||||||
|
thermal_action=('killall', '-INT', 'sysbench'),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run Sysbench test if necessary
|
# Run sysbench
|
||||||
run_sysbench = (
|
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=log_path)
|
||||||
not aborted and sensors.cpu_max_temp() >= cfg.hw.CPU_FAILURE_TEMP
|
proc, filehandle = hw_cpu.start_sysbench(log_path=log_path)
|
||||||
)
|
|
||||||
if run_sysbench:
|
|
||||||
LOG.info('CPU Test (Sysbench)')
|
|
||||||
cli.print_standard('Letting CPU cooldown more...')
|
|
||||||
std.sleep(10)
|
|
||||||
state.ui.clear_current_pane()
|
|
||||||
cli.print_info('Running alternate stress test')
|
|
||||||
print('')
|
|
||||||
sysbench_log = prime_log.with_name('sysbench.log')
|
|
||||||
sysbench_log.touch()
|
|
||||||
state.ui.remove_all_worker_panes()
|
|
||||||
state.ui.add_worker_pane(lines=10, watch_cmd='tail', watch_file=sysbench_log)
|
|
||||||
proc_sysbench, filehandle_sysbench = hw_cpu.start_sysbench(
|
|
||||||
sensors,
|
|
||||||
sensors_out,
|
|
||||||
log_path=sysbench_log,
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
print_countdown(proc=proc_sysbench, seconds=test_minutes*60)
|
print_countdown(proc=proc, seconds=test_minutes*60)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Assuming the sysbench process wasn't found and proc was set to None
|
# Assuming the sysbench process wasn't found and proc was set to None
|
||||||
LOG.error('Failed to find sysbench process', exc_info=True)
|
LOG.error('Failed to find sysbench process', exc_info=True)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
aborted = True
|
aborted = True
|
||||||
hw_cpu.stop_sysbench(proc_sysbench, filehandle_sysbench)
|
hw_cpu.stop_sysbench(proc, filehandle)
|
||||||
|
|
||||||
|
# Get cooldown temp
|
||||||
|
if 'Cooldown' not in state.sensors.temp_labels:
|
||||||
|
state.ui.clear_current_pane()
|
||||||
|
cli.print_standard('Letting CPU cooldown...')
|
||||||
|
std.sleep(5)
|
||||||
|
cli.print_standard('Saving cooldown temps...')
|
||||||
|
state.sensors.save_average_temps(temp_label='Cooldown', seconds=5)
|
||||||
|
|
||||||
# Update progress
|
# Update progress
|
||||||
# NOTE: CPU critical temp check isn't really necessary
|
test_object.report.append(ansi.color_string('Sysbench', 'BLUE'))
|
||||||
# Hard to imagine it wasn't hit during Prime95 but was in sysbench
|
if aborted:
|
||||||
if sensors.cpu_reached_critical_temp() or aborted:
|
test_object.set_status('Aborted')
|
||||||
test_cooling_obj.set_status('Aborted')
|
test_object.report.append(ansi.color_string(' Aborted.', 'YELLOW'))
|
||||||
test_mprime_obj.set_status('Aborted')
|
|
||||||
state.update_progress_file()
|
state.update_progress_file()
|
||||||
|
elif state.sensors.cpu_reached_critical_temp():
|
||||||
# Check Cooling results
|
test_object.set_status('Aborted')
|
||||||
test_cooling_obj.report.append(ansi.color_string('Temps', 'BLUE'))
|
test_object.report.append(
|
||||||
hw_cpu.check_cooling_results(test_cooling_obj, sensors, run_sysbench)
|
ansi.color_string(' Aborted due to temps.', 'YELLOW'),
|
||||||
|
|
||||||
# Post results to osTicket
|
|
||||||
if not state.ost.disabled:
|
|
||||||
_failed = test_cooling_obj.failed or test_mprime_obj.failed
|
|
||||||
cli.print_info('Posting results to osTicket...')
|
|
||||||
state.cpu_max_temp = sensors.cpu_max_temp()
|
|
||||||
state.ost.post_response(
|
|
||||||
hw_osticket.build_report(state.system, 'CPU'),
|
|
||||||
color='Diags FAIL' if _failed else 'Diags',
|
|
||||||
)
|
)
|
||||||
|
elif proc.returncode not in (-15, -2, 0):
|
||||||
# Cleanup
|
# NOTE: Return codes:
|
||||||
|
# 0 == Completed w/out issue
|
||||||
|
# -2 == Stopped with INT signal
|
||||||
|
# -15 == Stopped with TERM signal
|
||||||
|
test_object.set_status('Failed')
|
||||||
|
test_object.report.append(f' Failed with return code: {proc.returncode}')
|
||||||
|
else:
|
||||||
|
test_object.set_status('Passed')
|
||||||
|
test_object.report.append(' Completed without issue.')
|
||||||
state.update_progress_file()
|
state.update_progress_file()
|
||||||
sensors.stop_background_monitor()
|
|
||||||
state.ui.clear_current_pane_height()
|
|
||||||
state.ui.remove_all_info_panes()
|
|
||||||
state.ui.remove_all_worker_panes()
|
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
|
state.ui.remove_all_worker_panes()
|
||||||
if aborted:
|
if aborted:
|
||||||
|
cpu_tests_end(state)
|
||||||
raise std.GenericAbort('Aborted')
|
raise std.GenericAbort('Aborted')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -859,6 +917,25 @@ def print_countdown(proc, seconds) -> None:
|
||||||
# Done
|
# Done
|
||||||
print('')
|
print('')
|
||||||
|
|
||||||
|
def run_cpu_tests(state, test_objects, test_mode=False) -> None:
|
||||||
|
"""Run selected CPU test(s)."""
|
||||||
|
state.update_progress_file()
|
||||||
|
cpu_tests_init(state)
|
||||||
|
for obj in test_objects:
|
||||||
|
func = globals()[TEST_GROUPS[obj.name]]
|
||||||
|
func(state, obj, test_mode=test_mode)
|
||||||
|
cpu_tests_end(state)
|
||||||
|
state.update_progress_file()
|
||||||
|
|
||||||
|
# Post results to osTicket
|
||||||
|
if not state.ost.disabled:
|
||||||
|
failed = any(test.failed for test in state.system.tests)
|
||||||
|
cli.print_info('Posting results to osTicket...')
|
||||||
|
state.ost.post_response(
|
||||||
|
hw_osticket.build_report(state.system, 'CPU'),
|
||||||
|
color='Diags FAIL' if failed else 'Diags',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_diags(
|
def run_diags(
|
||||||
state: State,
|
state: State,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ from dataclasses import dataclass, field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from wk.cfg.main import KIT_NAME_SHORT
|
from wk.cfg.main import KIT_NAME_SHORT
|
||||||
from wk.cfg.python import DATACLASS_DECORATOR_KWARGS
|
|
||||||
from wk.exe import get_json_from_command, run_program
|
from wk.exe import get_json_from_command, run_program
|
||||||
from wk.hw.test import Test
|
from wk.hw.test import Test
|
||||||
from wk.hw.smart import (
|
from wk.hw.smart import (
|
||||||
|
|
@ -32,7 +31,7 @@ WK_LABEL_REGEX = re.compile(
|
||||||
|
|
||||||
|
|
||||||
# Classes
|
# Classes
|
||||||
@dataclass(**DATACLASS_DECORATOR_KWARGS)
|
@dataclass(slots=True)
|
||||||
class Disk:
|
class Disk:
|
||||||
"""Object for tracking disk specific data."""
|
"""Object for tracking disk specific data."""
|
||||||
attributes: dict[Any, dict] = field(init=False, default_factory=dict)
|
attributes: dict[Any, dict] = field(init=False, default_factory=dict)
|
||||||
|
|
|
||||||
|
|
@ -187,9 +187,7 @@ def update_checkboxes(state, num_disk_tests):
|
||||||
|
|
||||||
# CPU max temp and pass/fail
|
# CPU max temp and pass/fail
|
||||||
if cpu_tests:
|
if cpu_tests:
|
||||||
state.ost.set_cpu_max_temp(
|
state.ost.set_cpu_max_temp(state.sensors.get_cpu_temp('Max'))
|
||||||
state.cpu_max_temp,
|
|
||||||
)
|
|
||||||
if any(t.failed for t in cpu_tests):
|
if any(t.failed for t in cpu_tests):
|
||||||
state.ost.set_flag_failed('CPU')
|
state.ost.set_flag_failed('CPU')
|
||||||
elif all(t.passed for t in cpu_tests):
|
elif all(t.passed for t in cpu_tests):
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from subprocess import CalledProcessError
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from wk.cfg.hw import CPU_CRITICAL_TEMP, SMC_IDS, TEMP_COLORS
|
from wk.cfg.hw import CPU_TEMPS, SMC_IDS, TEMP_COLORS
|
||||||
from wk.exe import run_program, start_thread
|
from wk.exe import run_program, start_thread
|
||||||
from wk.io import non_clobber_path
|
from wk.io import non_clobber_path
|
||||||
from wk.std import PLATFORM, sleep
|
from wk.std import PLATFORM, sleep
|
||||||
|
|
@ -41,6 +41,7 @@ class Sensors():
|
||||||
self.background_thread: Thread | None = None
|
self.background_thread: Thread | None = None
|
||||||
self.data: dict[Any, Any] = get_sensor_data()
|
self.data: dict[Any, Any] = get_sensor_data()
|
||||||
self.out_path: pathlib.Path | str | None = None
|
self.out_path: pathlib.Path | str | None = None
|
||||||
|
self.temp_labels: set = set(['Current', 'Max'])
|
||||||
|
|
||||||
def clear_temps(self) -> None:
|
def clear_temps(self) -> None:
|
||||||
"""Clear saved temps but keep structure"""
|
"""Clear saved temps but keep structure"""
|
||||||
|
|
@ -49,26 +50,8 @@ class Sensors():
|
||||||
for source_data in sources.values():
|
for source_data in sources.values():
|
||||||
source_data['Temps'] = []
|
source_data['Temps'] = []
|
||||||
|
|
||||||
def cpu_max_temp(self) -> float:
|
|
||||||
"""Get max temp from any CPU source, returns float.
|
|
||||||
|
|
||||||
NOTE: If no temps are found this returns zero.
|
|
||||||
"""
|
|
||||||
max_temp = 0.0
|
|
||||||
|
|
||||||
# Check all CPU Temps
|
|
||||||
for section, adapters in self.data.items():
|
|
||||||
if not section.startswith('CPU'):
|
|
||||||
continue
|
|
||||||
for sources in adapters.values():
|
|
||||||
for source_data in sources.values():
|
|
||||||
max_temp = max(max_temp, source_data.get('Max', 0))
|
|
||||||
|
|
||||||
# Done
|
|
||||||
return max_temp
|
|
||||||
|
|
||||||
def cpu_reached_critical_temp(self) -> bool:
|
def cpu_reached_critical_temp(self) -> bool:
|
||||||
"""Check if CPU reached CPU_CRITICAL_TEMP, returns bool."""
|
"""Check if CPU exceeded critical temp, returns bool."""
|
||||||
for section, adapters in self.data.items():
|
for section, adapters in self.data.items():
|
||||||
if not section.startswith('CPU'):
|
if not section.startswith('CPU'):
|
||||||
# Limit to CPU temps
|
# Limit to CPU temps
|
||||||
|
|
@ -77,7 +60,7 @@ class Sensors():
|
||||||
# Ugly section
|
# Ugly section
|
||||||
for sources in adapters.values():
|
for sources in adapters.values():
|
||||||
for source_data in sources.values():
|
for source_data in sources.values():
|
||||||
if source_data.get('Max', -1) >= CPU_CRITICAL_TEMP:
|
if source_data.get('Max', -1) > CPU_TEMPS['Critical']:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Didn't return above so temps are within the threshold
|
# Didn't return above so temps are within the threshold
|
||||||
|
|
@ -119,6 +102,25 @@ class Sensors():
|
||||||
# Done
|
# Done
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
def get_cpu_temp(self, label) -> float:
|
||||||
|
"""Get temp for label from any CPU source, returns float.
|
||||||
|
|
||||||
|
NOTE: This returns the highest value for the label.
|
||||||
|
NOTE 2: If no temps are found this returns zero.
|
||||||
|
"""
|
||||||
|
max_temp = 0.0
|
||||||
|
|
||||||
|
# Check all CPU Temps
|
||||||
|
for section, adapters in self.data.items():
|
||||||
|
if not section.startswith('CPU'):
|
||||||
|
continue
|
||||||
|
for sources in adapters.values():
|
||||||
|
for source_data in sources.values():
|
||||||
|
max_temp = max(max_temp, source_data.get(label, 0))
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return float(max_temp)
|
||||||
|
|
||||||
def monitor_to_file(
|
def monitor_to_file(
|
||||||
self, out_path, alt_max=None,
|
self, out_path, alt_max=None,
|
||||||
exit_on_thermal_limit=True, temp_labels=None,
|
exit_on_thermal_limit=True, temp_labels=None,
|
||||||
|
|
@ -136,6 +138,7 @@ class Sensors():
|
||||||
temp_labels = ['Current', 'Max']
|
temp_labels = ['Current', 'Max']
|
||||||
if alt_max:
|
if alt_max:
|
||||||
temp_labels.append(alt_max)
|
temp_labels.append(alt_max)
|
||||||
|
self.temp_labels.add(alt_max)
|
||||||
|
|
||||||
# Start loop
|
# Start loop
|
||||||
while True:
|
while True:
|
||||||
|
|
@ -158,6 +161,7 @@ class Sensors():
|
||||||
def save_average_temps(self, temp_label, seconds=10) -> None:
|
def save_average_temps(self, temp_label, seconds=10) -> None:
|
||||||
"""Save average temps under temp_label over provided seconds.."""
|
"""Save average temps under temp_label over provided seconds.."""
|
||||||
self.clear_temps()
|
self.clear_temps()
|
||||||
|
self.temp_labels.add(temp_label)
|
||||||
|
|
||||||
# Get temps
|
# Get temps
|
||||||
for _ in range(seconds):
|
for _ in range(seconds):
|
||||||
|
|
@ -200,6 +204,10 @@ class Sensors():
|
||||||
|
|
||||||
def stop_background_monitor(self) -> None:
|
def stop_background_monitor(self) -> None:
|
||||||
"""Stop background thread."""
|
"""Stop background thread."""
|
||||||
|
# Bail early
|
||||||
|
if self.background_thread is None:
|
||||||
|
return
|
||||||
|
|
||||||
self.out_path.with_suffix('.stop').touch()
|
self.out_path.with_suffix('.stop').touch()
|
||||||
self.background_thread.join()
|
self.background_thread.join()
|
||||||
|
|
||||||
|
|
@ -210,6 +218,8 @@ class Sensors():
|
||||||
def update_sensor_data(
|
def update_sensor_data(
|
||||||
self, alt_max=None, exit_on_thermal_limit=True) -> None:
|
self, alt_max=None, exit_on_thermal_limit=True) -> None:
|
||||||
"""Update sensor data via OS-specific means."""
|
"""Update sensor data via OS-specific means."""
|
||||||
|
if alt_max:
|
||||||
|
self.temp_labels.add(alt_max)
|
||||||
if PLATFORM == 'Darwin':
|
if PLATFORM == 'Darwin':
|
||||||
self.update_sensor_data_macos(alt_max, exit_on_thermal_limit)
|
self.update_sensor_data_macos(alt_max, exit_on_thermal_limit)
|
||||||
elif PLATFORM == 'Linux':
|
elif PLATFORM == 'Linux':
|
||||||
|
|
@ -236,7 +246,7 @@ class Sensors():
|
||||||
|
|
||||||
# Raise exception if thermal limit reached
|
# Raise exception if thermal limit reached
|
||||||
if exit_on_thermal_limit and section == 'CPUTemps':
|
if exit_on_thermal_limit and section == 'CPUTemps':
|
||||||
if source_data['Current'] >= CPU_CRITICAL_TEMP:
|
if source_data['Current'] > CPU_TEMPS['Critical']:
|
||||||
raise ThermalLimitReachedError('CPU temps reached limit')
|
raise ThermalLimitReachedError('CPU temps reached limit')
|
||||||
|
|
||||||
def update_sensor_data_macos(
|
def update_sensor_data_macos(
|
||||||
|
|
@ -263,7 +273,7 @@ class Sensors():
|
||||||
|
|
||||||
# Raise exception if thermal limit reached
|
# Raise exception if thermal limit reached
|
||||||
if exit_on_thermal_limit and section == 'CPUTemps':
|
if exit_on_thermal_limit and section == 'CPUTemps':
|
||||||
if source_data['Current'] >= CPU_CRITICAL_TEMP:
|
if source_data['Current'] > CPU_TEMPS['Critical']:
|
||||||
raise ThermalLimitReachedError('CPU temps reached limit')
|
raise ThermalLimitReachedError('CPU temps reached limit')
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -420,7 +430,7 @@ def get_sensor_data_macos() -> dict[Any, Any]:
|
||||||
|
|
||||||
def get_temp_str(temp, colored=True) -> str:
|
def get_temp_str(temp, colored=True) -> str:
|
||||||
"""Get colored string based on temp, returns str."""
|
"""Get colored string based on temp, returns str."""
|
||||||
temp_color = None
|
temp_color = ''
|
||||||
|
|
||||||
# Safety check
|
# Safety check
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from dataclasses import dataclass, field
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from wk.cfg.hw import KNOWN_RAM_VENDOR_IDS
|
from wk.cfg.hw import KNOWN_RAM_VENDOR_IDS
|
||||||
from wk.cfg.python import DATACLASS_DECORATOR_KWARGS
|
|
||||||
from wk.exe import get_json_from_command, run_program
|
from wk.exe import get_json_from_command, run_program
|
||||||
from wk.hw.test import Test
|
from wk.hw.test import Test
|
||||||
from wk.std import PLATFORM, bytes_to_string, string_to_bytes
|
from wk.std import PLATFORM, bytes_to_string, string_to_bytes
|
||||||
|
|
@ -21,7 +20,7 @@ from wk.ui import ansi
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(**DATACLASS_DECORATOR_KWARGS)
|
@dataclass(slots=True)
|
||||||
class System:
|
class System:
|
||||||
"""Object for tracking system specific hardware data."""
|
"""Object for tracking system specific hardware data."""
|
||||||
cpu_description: str = field(init=False)
|
cpu_description: str = field(init=False)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
|
|
||||||
from wk.cfg.python import DATACLASS_DECORATOR_KWARGS
|
@dataclass(slots=True)
|
||||||
|
|
||||||
@dataclass(**DATACLASS_DECORATOR_KWARGS)
|
|
||||||
class Test:
|
class Test:
|
||||||
"""Object for tracking test specific data."""
|
"""Object for tracking test specific data."""
|
||||||
dev: Any
|
dev: Any
|
||||||
|
|
@ -14,7 +12,6 @@ class Test:
|
||||||
name: str
|
name: str
|
||||||
disabled: bool = field(init=False, default=False)
|
disabled: bool = field(init=False, default=False)
|
||||||
failed: bool = field(init=False, default=False)
|
failed: bool = field(init=False, default=False)
|
||||||
hidden: bool = False
|
|
||||||
passed: bool = field(init=False, default=False)
|
passed: bool = field(init=False, default=False)
|
||||||
report: list[str] = field(init=False, default_factory=list)
|
report: list[str] = field(init=False, default_factory=list)
|
||||||
status: str = field(init=False, default='Pending')
|
status: str = field(init=False, default='Pending')
|
||||||
|
|
@ -28,7 +25,7 @@ class Test:
|
||||||
self.status = status
|
self.status = status
|
||||||
|
|
||||||
|
|
||||||
@dataclass(**DATACLASS_DECORATOR_KWARGS)
|
@dataclass(slots=True)
|
||||||
class TestGroup:
|
class TestGroup:
|
||||||
"""Object for tracking groups of tests."""
|
"""Object for tracking groups of tests."""
|
||||||
name: str
|
name: str
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ class Menu():
|
||||||
# Display item in YELLOW
|
# Display item in YELLOW
|
||||||
disabled = True
|
disabled = True
|
||||||
checkmark = '*'
|
checkmark = '*'
|
||||||
if 'DISPLAY' in os.environ or PLATFORM == 'Darwin':
|
if 'CONEMUPID' in os.environ or 'DISPLAY' in os.environ or PLATFORM == 'Darwin':
|
||||||
checkmark = '✓'
|
checkmark = '✓'
|
||||||
display_name = f'{index if index else name[:1].upper()}: '
|
display_name = f'{index if index else name[:1].upper()}: '
|
||||||
if not (index and index >= 10):
|
if not (index and index >= 10):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue