diff --git a/ehci4intel-main/app.py b/ehci4intel-main/app.py new file mode 100644 index 0000000000000000000000000000000000000000..c5f09b5393ad1315c1daa1179ba17f27f837adb7 --- /dev/null +++ b/ehci4intel-main/app.py @@ -0,0 +1,521 @@ +from PyQt5.QtWidgets import * +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from pyqtgraph import * +import psutil +import re +import matplotlib.pyplot as plt +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QComboBox, QWidget, QMessageBox +import numpy as np +from scipy.signal import savgol_filter +import sys +import os +import os.path +import time +import ctypes +import win32com.client +import subprocess +import pyqtgraph as pg +import pandas as pd +from PyQt5.QtGui import QIcon, QPixmap + + +class Window(QMainWindow): + POWER_GADGET_PATH = None + LOG_FILE = None + + def __init__(self): + super().__init__() + self.setWindowTitle("EHCI") + stylesheet = open("syle.css", "r").read() + self.setGeometry(100, 100, 1280, 800) + self.setup_ui() + self.update_data() + self.graph_canvas = FigureCanvas(plt.Figure()) + self.setStyleSheet(stylesheet) + + icon = QPixmap('EHCI.png') + icon = icon.scaled(120, 120, aspectRatioMode=QtCore.Qt.KeepAspectRatio) # Adjust the size (64, 64) to the desired dimensions + self.setWindowIcon(QIcon('EHCI.png')) + self.energy_data = [] + + + + + self.app_plot_widgets = {} + + def add_new_plot_widget(self, title): + plot_widget = pg.PlotWidget() + plot_widget.setTitle(title) + self.graph_layout.addWidget(plot_widget) + return plot_widget + + def setup_ui(self): + # Set the main window layout + main_layout = QVBoxLayout() + + # Add a horizontal layout for buttons and labels + top_layout = QHBoxLayout() + + # Button to set the Intel Power Gadget path + set_path_btn = QPushButton("Set Power Gadget Path") + set_path_btn.clicked.connect(self.set_power_gadget_path) + top_layout.addWidget(set_path_btn) + + # Button to monitor an app + monitor_app_btn = QPushButton("Monitor App") + monitor_app_btn.clicked.connect(self.monitor_app) + top_layout.addWidget(monitor_app_btn) + + # Button to create a log file + create_log_btn = QPushButton("Create Log") + create_log_btn.clicked.connect(self.create_log) + top_layout.addWidget(create_log_btn) + + # Button to generate an energy report + generate_report_btn = QPushButton("Generate Energy Report") + generate_report_btn.clicked.connect(self.generate_energy_report) + top_layout.addWidget(generate_report_btn) + + # Button to compare apps + compare_apps_btn = QPushButton("Compare Apps") + compare_apps_btn.clicked.connect(self.compare_apps) + top_layout.addWidget(compare_apps_btn) + + # Button to exit the application + exit_app_btn = QPushButton("Exit") + exit_app_btn.clicked.connect(self.exit_app) + top_layout.addWidget(exit_app_btn) + + # Add the top layout to the main layout + main_layout.addLayout(top_layout) + + # Add battery information labels + self.battery_name_label = QLabel() + main_layout.addWidget(self.battery_name_label) + self.estimated_charge_remaining_label = QLabel() + main_layout.addWidget(self.estimated_charge_remaining_label) + self.design_capacity_label = QLabel() + main_layout.addWidget(self.design_capacity_label) + self.full_charge_capacity_label = QLabel() + main_layout.addWidget(self.full_charge_capacity_label) + self.battery_capacity_label = QLabel() + main_layout.addWidget(self.battery_capacity_label) + + self.user_type_label = QLabel("User Type:", self) + self.user_type_label.move(20, 130) # Adjust the position as needed + main_layout.addWidget(self.user_type_label) + + self.graph_layout = QVBoxLayout() + self.setLayout(self.graph_layout) + + + self.user_type_combobox = QComboBox(self) + self.user_type_combobox.addItem("Normal") + self.user_type_combobox.addItem("Special Needs") + self.user_type_combobox.move(100, 130) # Adjust the position as needed + main_layout.addWidget(self.user_type_combobox) + + self.button = QPushButton("Perform Action", self) + self.button.move(20, 170) # Adjust the position as needed + self.button.clicked.connect(self.on_button_click) + main_layout.addWidget(self.button) + + + # Add power and energy consumption labels + self.power_label = QLabel() + main_layout.addWidget(self.power_label) + self.energy_consumption_label = QLabel() + main_layout.addWidget(self.energy_consumption_label) + + # Add the graph canvas + #self.graph_canvas = FigureCanvas(plt.Figure()) + #self.graph_canvas.setGeometry(320, 10, 960, 600) + #main_layout.addWidget(self.graph_canvas) + + self.app_name_label = QLabel() + main_layout.addWidget(self.app_name_label) + + self.energy_consumption_label = QLabel("", self) + main_layout.addWidget(self.energy_consumption_label) + + self.energy_consumption_graph = pg.PlotWidget(title="Energy Consumption") + main_layout.addWidget(self.energy_consumption_graph) + + # Add this line to store multiple monitored apps + self.monitored_apps = [] + self.monitored_apps_label = QLabel("", self) + self.monitored_apps_energy = {} + main_layout.addWidget(self.monitored_apps_label) + + # Set the main layout for the window + central_widget = QWidget() + central_widget.setLayout(main_layout) + self.setCentralWidget(central_widget) + main_layout.addWidget(central_widget) + + + self.show() + + # Set up a timer to update data every second + self.update_timer = QTimer(self) + self.update_timer.timeout.connect(self.update_data) + self.update_timer.start(1000) + + + + def on_button_click(self): + selected_user_type = self.user_type_combobox.currentText() + if selected_user_type == "Normal": + self.perform_action_for_normal_user() + elif selected_user_type == "Special Needs": + self.perform_action_for_special_needs_user() + def perform_action_for_normal_user(self): + # Add the code to perform the action for normal users here + print("Performing action for a normal user") + + def perform_action_for_special_needs_user(self): + # Add the code to perform the action for users with special needs here + print("Performing action for a user with special needs") + + + def update_ui(self, app_name): + # Get energy consumption details for the selected app + energy_data = self.energy_data[app_name] + + total_energy = sum(self.energy_data.values()) + energy_percentage = (energy_data / total_energy) * 100 + + # Calculate average power usage + avg_power_usage = energy_data / self.monitoring_duration + + # Calculate estimated battery drain time + battery = psutil.sensors_battery() + battery_drain_time = battery.percent / (energy_data / 100) if battery else 0 + + # Update labels + self.energy_absolute_label.setText(f"Energy Consumption (Wh): {energy_data:.2f}") + self.energy_percentage_label.setText(f"Energy Consumption (%): {energy_percentage:.2f}") + self.avg_power_usage_label.setText(f"Average Power Usage (W): {avg_power_usage:.2f}") + self.battery_drain_time_label.setText(f"Estimated Battery Drain Time (h): {battery_drain_time:.2f}") + + # Update the graph + self.update_graph(app_name) + + # Update system-wide metrics + self.cpu_label.setText(f"CPU Utilization: {psutil.cpu_percent()}%") + self.memory_label.setText(f"Memory Utilization: {psutil.virtual_memory().percent}%") + self.app_dropdown.currentTextChanged.connect(self.update_ui) + + def update_data(self): + self.get_battery_data() + if self.POWER_GADGET_PATH: + power_data = self.get_power_data(self.POWER_GADGET_PATH, 1) + if 'Processor Power_0(Watt)' in power_data: + power = float(power_data['Processor Power_0(Watt)']) + self.power_label.setText(f"Processor Power: {power:.2f} W") + energy_consumption = power * 1 / 3600 + self.energy_consumption_label.setText(f"Energy Consumption: {energy_consumption:.5f} Wh") + + self.energy_data.append(energy_consumption) + self.energy_consumption_graph.plot(self.energy_data, clear=True, pen="b") + + def update_app_graph(self, app_name, data): + app_plot_widget = self.app_plot_widgets[app_name] + app_plot_widget.plot(data, clear=True) + + def update_graph(self, app_name): + # Clear the current graph + self.graph_canvas.figure.clf() + + # Create a new graph + ax = self.graph_canvas.figure.subplots() + + # Get power usage data for the selected app + power_data = self.power_data[app_name] + + # Plot the power usage over time + ax.plot(power_data["timestamps"], power_data["values"], label=app_name) + ax.set_xlabel("Time") + ax.set_ylabel("Power Usage (W)") + ax.set_title("Power Usage Over Time") + ax.legend() + + # Refresh the graph + self.graph_canvas.draw() + + def get_battery_data(self): + wmi = win32com.client.GetObject("winmgmts:") + battery_data = wmi.ExecQuery("Select * from Win32_Battery") + + batteries = [] + for battery in battery_data: + batteries.append(battery) + self.battery_name_label.setText(f"Battery Name: {battery.Name}") + self.estimated_charge_remaining_label.setText(f"Estimated Charge Remaining: {battery.EstimatedChargeRemaining}%") + self.design_capacity_label.setText(f"Design Capacity: {battery.DesignCapacity} mWh") + self.full_charge_capacity_label.setText(f"Full Charge Capacity: {battery.FullChargeCapacity} mWh") + + # Read and show battery capacity + if battery.FullChargeCapacity is not None and battery.DesignCapacity is not None: + battery_capacity = (battery.FullChargeCapacity / battery.DesignCapacity) * 100 + self.battery_capacity_label.setText(f'Battery capacity: {battery_capacity:.2f}%') + else: + self.battery_capacity_label.setText('Battery capacity information unavailable') + + return batteries + def is_app_running(app_name): + for process in psutil.process_iter(['name']): + if process.info['name'] == app_name: + return True + return False + + + def get_power_data(self, power_gadget_path, duration): + script_dir = os.path.dirname(os.path.abspath(__file__)) + power_log_path = os.path.join(power_gadget_path, "PowerLog3.0.exe") + log_file = os.path.join(script_dir, "power_log.csv") + command = f'"{power_log_path}" -duration {duration} -file "{log_file}"' + + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + time.sleep(duration + 1) # Wait for the logging process to finish + process.terminate() + + while not os.path.exists(log_file): + time.sleep(0.1) + + with open(log_file, "r") as f: + lines = f.readlines() + headers = lines[0].strip().split(',') + values = lines[1].strip().split(',') + + power_data = {} + for i in range(len(headers)): + power_data[headers[i]] = values[i] + + os.remove(log_file) + return power_data + + def set_power_gadget_path(self): + folder_path = QFileDialog.getExistingDirectory(self, "Select directory") + + if folder_path: + self.POWER_GADGET_PATH = folder_path + print(f"PowerLog3.0 path set to {self.POWER_GADGET_PATH}") + + + def create_log(self): + file_name, _ = QFileDialog.getSaveFileName(self, "Save file", "log.csv", "csv files (*.csv);;All Files (*)") + + if file_name: + if (os.path.exists(file_name)): + os.remove(file_name) + + f = open(file_name, "x") + f.close() + self.LOG_FILE = file_name + + + def get_energy_consumption(self, app_path): + # Run the app with the powerlog.bat script and log the energy consumption + powerlog_exe = os.path.join(self.POWER_GADGET_PATH, "PowerLog3.0.exe") + log_file = "power_log.csv" + command = f'"{powerlog_exe}" -duration 10 -file "{log_file}"' + + batch_file_content = f""" +@echo off +start /b "" cmd /c "{command} >> {log_file}" +start /wait "" "{app_path}" +""" + + batch_file_path = os.path.join(os.path.dirname(log_file), "monitoring.bat") + + with open(batch_file_path, "w") as f: + f.write(batch_file_content) + + try: + os.startfile(batch_file_path, "runas") + except Exception as e: + QMessageBox.critical(self, "Error", f"An error occurred while running the monitoring process: {e}") + finally: + os.remove(batch_file_path) + + # Calculate the energy consumption of the app + while not os.path.exists(log_file): + time.sleep(0.1) + + with open(log_file, "r") as f: + lines = f.readlines() + headers = lines[0].strip().split(',') + values = lines[1].strip().split(',') + + power_data = {} + for i in range(len(headers)): + power_data[headers[i]] = float(values[i]) + + os.remove(log_file) + energy_consumption = sum(self.energy_data) + return energy_consumption + + def update_monitored_apps_label(self): + app_names = [os.path.basename(app_path) for app_path in self.monitored_apps] + app_names_text = "\n".join(app_names) + self.app_name_label.setText(f"Monitored Apps:\n{app_names_text}") + + def mess_with_app(self): + self.get_battery_data() + while True: + power_data = self.get_power_data(self.POWER_GADGET_PATH, 0.1) + if 'Processor Power_0(Watt)' in power_data: + power = float(power_data['Processor Power_0(Watt)']) * random.uniform(0.5, 1.5) + self.power_label.setText(f"Processor Power: {power:.2f} W") + energy_consumption = power * 0.1 / 3600 + self.energy_consumption_label.setText(f"Energy Consumption: {energy_consumption:.5f} Wh") + self.energy_data.append(energy_consumption) + window_size = 5 + if len(self.energy_data) > window_size: + smoothed_energy_data = savgol_filter(self.energy_data, window_length=11, polyorder=3) + self.energy_consumption_graph.plot(smoothed_energy_data, clear=True, pen="b") + else: + self.energy_consumption_graph.plot(self.energy_data, clear=True, pen="b") + time.sleep(0.1) + def exit_app(self): + sys.exit() + + def is_admin(): + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + return False + def generate_energy_report(self): + report_file = os.path.join(self.current_directory, f"energy_report_{self.report_counter}.html") + command = f'powercfg /ENERGY /OUTPUT "{report_file}" /DURATION 5' + + if is_admin(): + try: + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + stdout, stderr = process.communicate() + print(f"Command output: {stdout.decode()}") # Debug line + print(f"Command error: {stderr.decode()}") # Debug line + except Exception as e: + QMessageBox.critical(self, "Error", f"An error occurred while running the energy monitoring process: {e}") + else: + # Re-run the script with admin rights + ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1) + + + time.sleep(sampling_interval) + + energy_usage = self.parse_energy_usage_from_report(report_file) + energy_usage_data.append(energy_usage) + + os.remove(report_file) + + return energy_usage_data + + def parse_energy_usage_from_report(self, report_file): + # Extract the energy usage value from the report_file + # This depends on the format of the energy report generated by the 'powercfg' command + energy_usage = 0 + + with open(report_file, "r") as f: + content = f.read() + # Assume energy usage is in the format "Energy usage (Wh): X" where X is a float + match = re.search(r"Energy usage \(Wh\):\s+([\d.]+)", content) + if match: + energy_usage = float(match.group(1)) + + return energy_usage + + def moving_average(data, window_size): + data_series = pd.Series(data) + windows = data_series.rolling(window=window_size) + moving_averages = windows.mean() + + return moving_averages.tolist() + + + def monitor_app(self, app_name): + app_path, _ = QFileDialog.getOpenFileName(self, "Choose exe", "", "exe files (*.exe)") + app_plot_widget = self.add_new_plot_widget(app_name) + + # Store the app's plot widget in a dictionary + self.app_plot_widgets[app_name] = app_plot_widget + if app_path: + if app_path not in self.monitored_apps: + self.monitored_apps.append(app_path) + app_name = os.path.basename(app_path) + print(f"Monitoring {app_name}") + self.update_monitored_apps_label() + energy_consumption = self.get_energy_consumption(app_path) + self.monitored_apps_energy[app_name] = energy_consumption + else: + QMessageBox.warning(self, "Warning", "The selected app is already being monitored.") + + # Add the following code to the monitor_app function + powerlog_exe = os.path.join(self.POWER_GADGET_PATH, "PowerLog3.0.exe") + log_file = "power_log.csv" + command = f'"{powerlog_exe}" -duration 10 -file "{log_file}"' + + batch_file_content = f""" +@echo off +start /b "" cmd /c "{command} >> {log_file}" +start /wait "" "{app_path}" +""" + + batch_file_path = os.path.join(os.path.dirname(log_file), "monitoring.bat") + + with open(batch_file_path, "w") as f: + f.write(batch_file_content) + + try: + os.startfile(batch_file_path, "runas") + energy_usage_data = self.generate_energy_report(app_path, num_samples=10, sampling_interval=1) + smoothed_data = self.moving_average(energy_usage_data, window_size=3) + + + except Exception as e: + QMessageBox.critical(self, "Error", f"An error occurred while running the monitoring process: {e}") + finally: + os.remove(batch_file_path) + def compare_apps(self): + if len(self.monitored_apps) < 2: + QMessageBox.warning(self, "Warning", "Please monitor at least two apps before comparing their energy consumption.") + return + + # Clear the current graph + self.graph_canvas.figure.clf() + + # Create a new graph + ax = self.graph_canvas.figure.subplots() + + app_names = list(self.monitored_apps_energy.keys()) + energy_consumption_values = list(self.monitored_apps_energy.values()) + + # Create a bar chart + bars = ax.bar(app_names, energy_consumption_values) + + # Add data labels on top of each bar + for bar in bars: + height = bar.get_height() + ax.annotate(f"{height:.2f}", + xy=(bar.get_x() + bar.get_width() / 2, height), + xytext=(0, 3), # 3 points vertical offset + textcoords="offset points", + ha="center", va="bottom") + + ax.set_xlabel("Applications") + ax.set_ylabel("Energy Consumption (Wh)") + ax.set_title("Energy Consumption Comparison") + + # Refresh the graph + self.graph_canvas.draw() + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = Window() + + sys.exit(app.exec()) + + \ No newline at end of file