diff --git a/tools/cfg_util/record/func.py b/tools/cfg_util/record/func.py index f1c979fc..782668f2 100644 --- a/tools/cfg_util/record/func.py +++ b/tools/cfg_util/record/func.py @@ -1,21 +1,20 @@ from typing import List from tools.cfg_util.record.manager import RecordingManager -from tools.cfg_util.record.layout import record_window +import PySimpleGUI as sg -async def start_recording(ips: List[str], file: str, interval: int = 10): +async def start_recording( + ips: List[str], file: str, record_window: sg.Window, interval: int = 10 +): record_window["start_recording"].update(visible=False) record_window["stop_recording"].update(visible=True) record_window["pause_recording"].update(visible=True) record_window["resume_recording"].update(visible=False) record_window["_placeholder"].update(visible=False) - await RecordingManager().record( - ips, - file, - ) + await RecordingManager().record(ips, file, record_window, interval=interval) -async def pause_recording(): +async def pause_recording(record_window): await RecordingManager().pause() record_window["resume_recording"].update(visible=True) record_window["start_recording"].update(visible=False) @@ -23,7 +22,7 @@ async def pause_recording(): record_window["pause_recording"].update(visible=False) -async def stop_recording(): +async def stop_recording(record_window): await RecordingManager().stop() record_window["start_recording"].update(visible=True) record_window["stop_recording"].update(visible=False) @@ -32,7 +31,7 @@ async def stop_recording(): record_window["_placeholder"].update(visible=True) -async def resume_recording(): +async def resume_recording(record_window): await RecordingManager().resume() record_window["start_recording"].update(visible=False) record_window["stop_recording"].update(visible=True) diff --git a/tools/cfg_util/record/layout.py b/tools/cfg_util/record/layout.py index a60275c7..d62f452c 100644 --- a/tools/cfg_util/record/layout.py +++ b/tools/cfg_util/record/layout.py @@ -104,4 +104,5 @@ def record_layout(): return record_layout -record_window = sg.Window("Record Miner Data", record_layout(), modal=True) +def get_record_window(): + return sg.Window("Record Miner Data", record_layout(), modal=True) diff --git a/tools/cfg_util/record/manager.py b/tools/cfg_util/record/manager.py index fec4e384..ed8cd8f6 100644 --- a/tools/cfg_util/record/manager.py +++ b/tools/cfg_util/record/manager.py @@ -1,6 +1,6 @@ import asyncio -from tools.cfg_util.record.layout import record_window +from tools.cfg_util.record.pdf import generate_pdf from miners.miner_factory import MinerFactory @@ -27,14 +27,15 @@ class RecordingManager(metaclass=Singleton): self.miners = [] self.output_file = None self.interval = 10 + self.record_window = None async def _check_pause(self): if self.state == PAUSING: self.state = PAUSED - record_window["record_status"].update("Paused.") + self.record_window["record_status"].update("Paused.") while not self.state == RESUMING and not self.state == STOPPING: await asyncio.sleep(0.1) - record_window["record_status"].update("Recording...") + self.record_window["record_status"].update("Recording...") async def _record_loop(self): while True: @@ -58,36 +59,39 @@ class RecordingManager(metaclass=Singleton): await asyncio.sleep(0.1) self.state = DONE - record_window["record_status"].update("Writing to file...") - await self.write_output() - record_window["record_status"].update("") + self.record_window["record_status"].update( + "Writing to file (this could take a minute)..." + ) + await asyncio.sleep(0.5) + await asyncio.create_task(self.write_output()) + self.record_window["record_status"].update("") async def write_output(self): - from pprint import pprint + await generate_pdf(self.data, self.output_file) - pprint(self.data) - - async def record(self, ips: List[str], output_file: str, interval: int = 10): + async def record( + self, ips: List[str], output_file: str, record_window, interval: int = 10 + ): + self.record_window = record_window for ip in ips: self.data[ip] = [] self.output_file = output_file self.interval = interval self.state = RECORDING - record_window["record_status"].update("Recording...") + self.record_window["record_status"].update("Recording...") async for miner in MinerFactory().get_miner_generator(ips): self.miners.append(miner) - print(miner) asyncio.create_task(self._record_loop()) async def pause(self): self.state = PAUSING - record_window["record_status"].update("Pausing...") + self.record_window["record_status"].update("Pausing...") async def resume(self): self.state = RESUMING - record_window["record_status"].update("Resuming...") + self.record_window["record_status"].update("Resuming...") async def stop(self): self.state = STOPPING - record_window["record_status"].update("Stopping...") + self.record_window["record_status"].update("Stopping...") diff --git a/tools/cfg_util/record/pdf.py b/tools/cfg_util/record/pdf.py new file mode 100644 index 00000000..701deb0a --- /dev/null +++ b/tools/cfg_util/record/pdf.py @@ -0,0 +1,177 @@ +from datetime import datetime, timedelta +from typing import List, Dict +from data import MinerData + + +import matplotlib.pyplot as plt +from matplotlib.ticker import MultipleLocator +from matplotlib.dates import DateFormatter +import numpy as np +from reportlab.lib import colors +from reportlab.lib.pagesizes import letter, inch +from reportlab.lib.styles import ( + ParagraphStyle, + TA_CENTER, # noqa - not declared in __all__ +) +from reportlab.lib.utils import ImageReader +from reportlab.platypus import ( + SimpleDocTemplate, + KeepInFrame, + Table, + Image, + Paragraph, + TableStyle, + PageBreak, + Spacer, +) +from io import BytesIO +from svglib.svglib import svg2rlg + + +async def generate_pdf(data: Dict[str, List[MinerData]], file_loc): + doc = SimpleDocTemplate( + file_loc, + pagesize=letter, + topMargin=0.25 * inch, + leftMargin=1 * inch, + rightMargin=1 * inch, + bottomMargin=1 * inch, + title=f"Recorded Data", + ) + + elements = [] + i = 0 + for item in data.keys(): + i += 1 + if not i == 1: + elements.append(PageBreak()) + page_elem = await generate_page(data[item]) + for elem in page_elem: + elements.append(elem) + + doc.build( + elements, + ) + + +async def generate_page(data): + title_style = ParagraphStyle( + "Title", + alignment=TA_CENTER, + fontSize=25, + spaceAfter=40, + spaceBefore=150, + fontName="Helvetica-Bold", + ) + + hr_graph = create_hr_graph(data) + fan_graph = create_fans_graph(data) + temp_graph = create_temp_graph(data) + title = Paragraph(data[0].ip, style=title_style) + + elements = [ + title, + await hr_graph, + Spacer(0, 40), + await temp_graph, + Spacer(0, 40), + await fan_graph, + ] + return elements + + +async def create_hr_graph(data): + fig, ax = plt.subplots(figsize=(6, 2)) + xpoints = [] + ypoints = [] + for item in data: + xpoints.append(item.datetime) + ypoints.append(item.hashrate) + for label in ax.get_xticklabels() + ax.get_yticklabels(): + label.set_fontsize(6) + ax.plot(xpoints, ypoints) + ax.set_ylim(0, max(ypoints) * 1.4) + date_form = DateFormatter("%H:%M:%S") + ax.xaxis.set_major_formatter(date_form) + ax.yaxis.set_major_formatter("{x:1.1f} TH/s") + ax.set_title("Hashrate", fontsize=15) + ax.yaxis.set_major_locator(MultipleLocator(5)) + ax.yaxis.set_minor_locator(MultipleLocator(1)) + + imgdata = BytesIO() + fig.savefig(imgdata, format="svg") + imgdata.seek(0) # rewind the data + drawing = svg2rlg(imgdata) + imgdata.close() + plt.close("all") + + hr_graph = KeepInFrame(375, 375, [Image(drawing)], hAlign="CENTER") + + return hr_graph + + +async def create_fans_graph(data): + fig, ax = plt.subplots(figsize=(6, 2)) + xpoints = [] + ypoints_f1 = [] + ypoints_f2 = [] + ypoints_f3 = [] + ypoints_f4 = [] + for item in data: + xpoints.append(item.datetime) + ypoints_f1.append(item.fan_1) + ypoints_f2.append(item.fan_2) + ypoints_f3.append(item.fan_3) + ypoints_f4.append(item.fan_4) + for label in ax.get_xticklabels() + ax.get_yticklabels(): + label.set_fontsize(6) + for ypoints in [ypoints_f1, ypoints_f2, ypoints_f3, ypoints_f4]: + if not ypoints == [-1 for x in range(len(ypoints))]: + ax.plot(xpoints, ypoints) + ax.set_ylim(0, 10000) + date_form = DateFormatter("%H:%M:%S") + ax.xaxis.set_major_formatter(date_form) + ax.yaxis.set_major_formatter("{x:1.0f} RPM") + ax.set_title("Fans", fontsize=15) + + imgdata = BytesIO() + fig.savefig(imgdata, format="svg") + imgdata.seek(0) # rewind the data + drawing = svg2rlg(imgdata) + imgdata.close() + plt.close("all") + + fans_graph = KeepInFrame(375, 375, [Image(drawing)], hAlign="CENTER") + + return fans_graph + + +async def create_temp_graph(data): + fig, ax = plt.subplots(figsize=(6, 2)) + # plt.figure() + xpoints = [] + ypoints = [] + for item in data: + xpoints.append(item.datetime) + ypoints.append(item.temperature_avg) + for label in ax.get_xticklabels() + ax.get_yticklabels(): + label.set_fontsize(6) + ax.plot(xpoints, ypoints) + ax.set_ylim(0, 130) + ax.yaxis.set_major_locator(MultipleLocator(20)) + ax.yaxis.set_minor_locator(MultipleLocator(5)) + date_form = DateFormatter("%H:%M:%S") + ax.xaxis.set_major_formatter(date_form) + ax.yaxis.set_major_formatter("{x:1.1f} C") + ax.set_title("Temperature", fontsize=15) + + imgdata = BytesIO() + fig.savefig(imgdata, format="svg") + imgdata.seek(0) # rewind the data + drawing = svg2rlg(imgdata) + imgdata.close() + plt.close("all") + + temp_graph = KeepInFrame(375, 375, [Image(drawing)], hAlign="CENTER") + + return temp_graph diff --git a/tools/cfg_util/record/ui.py b/tools/cfg_util/record/ui.py index c065d562..62103270 100644 --- a/tools/cfg_util/record/ui.py +++ b/tools/cfg_util/record/ui.py @@ -1,7 +1,7 @@ import asyncio import PySimpleGUI as sg -from tools.cfg_util.record.layout import record_window +from tools.cfg_util.record.layout import get_record_window from tools.cfg_util.record.func import ( start_recording, stop_recording, @@ -11,8 +11,9 @@ from tools.cfg_util.record.func import ( async def record_ui(ips: list): - # if not len(ips) > 0: - # return + if not len(ips) > 0: + return + record_window = get_record_window() while True: event, values = record_window.read(0.001) if event in (None, "Close", sg.WIN_CLOSED): @@ -20,13 +21,15 @@ async def record_ui(ips: list): if event == "start_recording": if values["record_file"]: - asyncio.create_task(start_recording(ips, values["record_file"])) + asyncio.create_task( + start_recording(ips, values["record_file"], record_window) + ) if event == "stop_recording": - asyncio.create_task(stop_recording()) + asyncio.create_task(stop_recording(record_window)) if event == "resume_recording": - asyncio.create_task(resume_recording()) + asyncio.create_task(resume_recording(record_window)) if event == "pause_recording": - asyncio.create_task(pause_recording()) + asyncio.create_task(pause_recording(record_window)) if event == "__TIMEOUT__": await asyncio.sleep(0.001)