From ee12663b602c98d71cc0bdf920ea13b91b2e5891 Mon Sep 17 00:00:00 2001 From: Jonas_Jones Date: Mon, 29 Aug 2022 18:46:10 +0200 Subject: [PATCH] removed stuff --- pc/main.py | 105 ----------------- pi-master/main.py | 283 ---------------------------------------------- pi-slave/main.py | 57 ---------- src/circuit.png | Bin 26443 -> 0 bytes 4 files changed, 445 deletions(-) delete mode 100644 pc/main.py delete mode 100644 pi-master/main.py delete mode 100644 pi-slave/main.py delete mode 100644 src/circuit.png diff --git a/pc/main.py b/pc/main.py deleted file mode 100644 index 38689d2..0000000 --- a/pc/main.py +++ /dev/null @@ -1,105 +0,0 @@ -VERSION = "0.1.1" - -from ftplib import FTP -from io import BytesIO -from time import sleep, time - -launch_time = time() - -print("Starting PI-MC-WATCHER v" + VERSION + " for PC and Mac") -print("Initializing...") - -# wehther or not to print the stats to the console -print_stats = True - -# The credentials for the ftp server. These must be tweaked. -hostname = "172.24.1.193" -username = "testuser" -password = "uwu" - -# defining the name of the file the stats are saved to -filename = "output.txt" - -print("Connecting to FTP server...") - -ftp = FTP(hostname, username, password) -ftp.encoding = "utf-8" - -print("Connected!") - -start_time = time() - -print("All setup!") - -# permits user to read console logs from above. Can be removed to accelerate start. -sleep(3) - -while True: - - with open(filename, "wb") as file: - # use FTP's RETR command to download the file - ftp.retrbinary(f"RETR {filename}", file.write) - - file = open(filename, 'r') - content = file.read() - content = content.split(";") - # it is possible for the program to fetch the output text file right inbetween when the program on the Master PI cleared it and before it writes the stats. - # This sends an error message in the rare case of this occuring. - try: - fanmode = content[0] - slave_fanmode = content[1] - fanspeed = content[2] - fanspeed2 = content[3] - thermal = content[4] - slave_thermal = content[5] - cpu_usage = content[6] - cpu_freq = content[7] - cpu_up = content[8] - total_ram = content[9] - ram_usage = content[10] - ram_free = content[11] - ram_percent = content[12] - swap_percent = content[13] - disk_percent = content[14] - - mc_motd = content[15] - mc_version = content[16] - mc_players_max = content[17] - mc_players_on = content[18] - mc_version_brand = content[19] - mc_plugins = content[20] - mc_playerlist = content[21] - except: - # The error message to display if the above explained happens - print("WARNING: Couldn't read the file's data. If this doesn't happen a lot, just ignore this warning.") - - - if print_stats: - print("---------- Uptime: " + str(cpu_up) + " ----------") - # loop_time and stop_time won't be available on the first run as they haven't been measured yet. Therefore this fallback is needed. - try: - print("Loop time:\t" + str(round(loop_time, 2)) + "\ts") - print("CMD Uptime:\t" + str(round(stop_time - launch_time, 2)) + "\ts") - except: - print("Loop time:\t--.--\ts") - print("CMD Uptime:\t--.--\ts") - print("CPU") - print("\tusage:\t" + str(cpu_usage) + "\t%") - print("\tfreq:\t" + str(cpu_freq) + "\tMHz") - print("\ttemp:\t" + str(thermal) + "\tC") - print("RAM") - print("\tusage:\t" + str(ram_percent) + "\t%") - print("\tswap :\t" + str(swap_percent) + "\t%") - print("Disks") - print("\tusage:\t" + str(disk_percent) + "\t%") - print("FANMODE") - print("\tmain:\t" + str(fanmode) + "\t%") - print("\tslave:\t" + str(slave_fanmode) + "\t%") - print("\tglobal:\t" + str(fanspeed) + "\t%") - print("\t2ndary:\t" + str(fanspeed2) + "\t%") - - sleep(2) - - stop_time = time() - loop_time = stop_time - start_time - start_time = time() \ No newline at end of file diff --git a/pi-master/main.py b/pi-master/main.py deleted file mode 100644 index d3f12f3..0000000 --- a/pi-master/main.py +++ /dev/null @@ -1,283 +0,0 @@ -VERSION = "0.1.1" - -from http import server -import RPi.GPIO as GPIO -import subprocess, os, time, psutil - -launch_time = time.time() - -# Wether or not to print system essential stats into the console (This can have a small to medium performance impact.) -print_stats = True -# Wehter or not to ignore the slave PI. This can be useful if no second PI is connected. -ignore_slave = True - -# Starting Messages -print("Starting PI-MC-WATCHER v" + VERSION + " for RPi") -print("Processing first loop. Each loop takes 5 seconds...") -print("\n") -if print_stats: - print("The system essential stats will be printed into the console. (This can be disabled at line 8 in the program file)") -else: - print("The system essential stats will NOT be printed into the console. (This can be enabled at line 8 in the program file)") - -print("\n") - -if ignore_slave: - print("Slave will be ignored. (This can be configured at line 10 in the program file)") -else: - print("Slave will NOT be ignored. (This can be configured at line 10 in the program file)") - -# Program Delay to allow reading the above printed messages -time.sleep(5) -print("\nStarting process MAIN") - -# GPIO setup -GPIO.setmode(GPIO.BCM) -GPIO.setwarnings(False) - -# Powering Status Pin to indicate that control program is up -GPIO.setup(4, GPIO.OUT) -GPIO.output(4, True) - -# setting up communication pins for binary fanmode transmission -GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) -GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) - -# setting up control pin for fancontrol -GPIO.setup(4, GPIO.OUT) - -# setting up power pin for second fancontrol -GPIO.setup(25, GPIO.OUT) - -# define default variables -> fan speed will be at max. if someting goes wrong with getting cpu temps -global thermal, fanmode, slave_fanmode, slave_thermal, i, file, cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent, mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list -thermal = -99 -fanmode = 100 -i = 0 - -file_path = "/home/testuser/output.txt" - -file = open(file_path, 'w') -file.close() - -cpu_usage = 0 -cpu_freq = 0 -cpu_up = 0 -total_ram = 0 -ram_usage = 0 -ram_free = 0 -ram_percent = 0 -swap_percent = 0 -disk_percent = 0 - -# check mc server status too. Set to False to disable Minecraft server module -check_mcserver = True -mc_ip = "127.0.0.1" -mc_port = "25565" - -mc_motd = "-" -mc_version = 0 -mc_players_max = 0 -mc_players_on = 0 -mc_version_brand = "-" -mc_plugins = [] -mc_player_list = [] - -# function to get cpu temperature on linux and macOS and decide on fan speed. -def temp(thermal, fanmode): - thermal = int(round(float(subprocess.check_output("cat /sys/class/thermal/thermal_zone0/temp", shell=True).rstrip())/1000,2)) - thermal = int(thermal) - if thermal <= 20: - fanmode = 0 - elif 20 < thermal <= 35: - fanmode = 25 - elif 35 < thermal <= 50: - fanmode = 50 - elif 50 < thermal <= 65: - fanmode = 75 - elif 65 < thermal: - fanmode = 100 - return thermal, fanmode - -# function to round values -def display(val): - return round(float(val),2) - -# function to get system informations -def sys_monitor(cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent): - #cpu_usage = str(round(float(os.popen('''grep 'cpu ' /proc/stat | awk '{usage=($2+$4)*100/($2+$4+$5)} END {print usage }' ''').readline()),2)) - cpu_usage = psutil.cpu_percent(interval=None) - cpu_freq = psutil.cpu_freq(percpu=False) - cpu_freq = cpu_freq.current - cpu_up = psutil.cpu_times() - cpu_up = cpu_up.user - - ram = psutil.virtual_memory() - #total_ram = str(round(display(subprocess.check_output("free | awk 'FNR == 2 {print $2/1000000}'", shell=True)))) - total_ram = ram.total - #ram_usage = int(subprocess.check_output("free | awk 'FNR == 2 {print $3/($3+$4)*100}'", shell=True)) - ram_usage = ram.used - #ram_free = str(100 - display(ram_usage)) - ram_free = ram.free - ram_percent = ram.percent - - swap = psutil.swap_memory() - swap_percent = swap.percent - - disk = psutil.disk_usage('/') - disk_percent = disk.percent - - - return cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent - -# function to get server status if enabled -def mcserver(mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list): - if check_mcserver: - # importing module mcstatus --> must be installed for this in order to work - from mcstatus import MinecraftServer - mcserver = MinecraftServer.lookup(mc_ip + ":" + mc_port) - try: - status = mcserver.status() - mc_motd = status.description - mc_version = status.version.name - mc_players_max = status.players.max - mc_players_on = status.players.online - except: - status = "offline" - mc_motd = "-" - mc_version = "-" - mc_players_max = "-" - mc_players_on = "-" - - # IMPORTANT! - # for the following code to work, the query must be enabled in the server config file ('server.properties' --> 'enable-query=True'). - # If you don't have access to that file but still want your server status, you can delete the following lines that contain the 'query' argument. - # This will provide you with a more limited status: - # the version brand (vanilla, fabric modded, bukkit, etc.), the plugin list (if any) and the list if players won't be visible in that case - try: - query = mcserver.query() - mc_version = query.software.version - mc_version_brand = query.software.brand - mc_plugins = query.software.plugins - mc_player_list = query.players.names - except: - query = "unreachable" - mc_version = "-" - mc_version_brand = "-" - mc_plugins = "-" - mc_player_list = "-" - - # if no query --> delete 'mc_version_brand', 'mc_plugins' and 'mc_player_list' from the return argument - # the line below should then look like THIS: return mc_motd, mc_version, mc_players_max, mc_players_on - return mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list - - -while True: - start_time = time.time() - # execute temperature function - - thermal, fanmode = temp(thermal, fanmode) - # determine fanmode of 2nd raspberry PI and guess CPU temps - if GPIO.input(17) == GPIO.HIGH and GPIO.input(18) == GPIO.HIGH: - slave_fanmode = 75 - slave_thermal = "60 - 70" - elif GPIO.input(17) == GPIO.HIGH and not GPIO.input(18) == GPIO.HIGH: - slave_fanmode = 50 - slave_thermal = "40 - 60" - elif not GPIO.input(17) == GPIO.HIGH and GPIO.input(18) == GPIO.HIGH: - slave_fanmode = 25 - slave_thermal = "0 - 40" - elif not GPIO.input(17) == GPIO.HIGH and not GPIO.input(18) == GPIO.HIGH: - slave_fanmode = 100 - slave_thermal = "70 - 100" - else: - slave_fanmode = 100 - slave_thermal = "-" - - # get other system infos and Minecraft Server stats - cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent = sys_monitor(cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent) - mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list = mcserver(mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list) - - # determine the PI with higher CPU temperature - if ignore_slave: - fanspeed = fanmode - else: - if slave_fanmode > fanmode: - fanspeed = slave_fanmode - else: - fanspeed = fanmode - - if fanmode < 50: - fanspeed2 = 0 - GPIO.output(25, False) - elif fanmode >= 50: - fanspeed2 = 100 - GPIO.output(25, True) - elif thermal >= 85: - os.system("sudo poweroff") - - i = 0 - - - if fanmode == 100: - GPIO.output(4, True) - time.sleep(2) - elif fanmode == 75: - while i < 6: - GPIO.output(4, True) - time.sleep(0.3) - GPIO.output(4, False) - time.sleep(0.1) - i = i + 1 - elif fanmode == 50: - while i < 6: - GPIO.output(4, True) - time.sleep(0.2) - GPIO.output(4, False) - time.sleep(0.2) - i = i + 1 - elif fanmode == 25: - while i < 6: - GPIO.output(4, True) - time.sleep(0.1) - GPIO.output(4, False) - time.sleep(0.3) - i = i + 1 - else: - GPIO.output(4, True) - time.sleep(2) - - # write all data to list - system_infos = [fanmode, slave_fanmode, fanspeed, fanspeed2, thermal, slave_thermal, cpu_usage, cpu_freq, cpu_up, total_ram, ram_usage, ram_free, ram_percent, swap_percent, disk_percent, mc_motd, mc_version, mc_players_max, mc_players_on, mc_version_brand, mc_plugins, mc_player_list] - - # delete all text in file and write list to file - file = open(file_path, 'r+') - file.truncate(0) - file.close() - file = open(file_path, 'w') - for e in system_infos: - file.write(str(e)) - file.write(";") - file.close() - - stop_time = time.time() - - if print_stats: - print("---------- Uptime: " + str(cpu_up) + " ----------") - print("Loop time:\t" + str(round(stop_time - start_time, 2)) + "\ts") - print("CMD Uptime:\t" + str(round(stop_time - launch_time, 2)) + "\ts") - print("CPU") - print("\tusage:\t" + str(cpu_usage) + "\t%") - print("\tfreq:\t" + str(cpu_freq) + "\tMHz") - print("\ttemp:\t" + str(thermal) + "\tC") - print("RAM") - print("\tusage:\t" + str(ram_percent) + "\t%") - print("\tswap :\t" + str(swap_percent) + "\t%") - print("Disks") - print("\tusage:\t" + str(disk_percent) + "\t%") - print("FANMODE") - print("\tmain:\t" + str(fanmode) + "\t%") - print("\tslave:\t" + str(slave_fanmode) + "\t%") - print("\tglobal:\t" + str(fanspeed) + "\t%") - print("\t2ndary:\t" + str(fanspeed2) + "\t%") - \ No newline at end of file diff --git a/pi-slave/main.py b/pi-slave/main.py deleted file mode 100644 index 0532fed..0000000 --- a/pi-slave/main.py +++ /dev/null @@ -1,57 +0,0 @@ -import RPi.GPIO as GPIO -import subprocess -import time - -# GPIO setup -GPIO.setmode(GPIO.BCM) -GPIO.setwarnings(False) - -# Powering Status Pin to indicate that control program is up -GPIO.setup(4, GPIO.OUT) -GPIO.output(4, True) - -# setting up communication pins for binary fanmode transmission -GPIO.setup(17, GPIO.OUT) -GPIO.setup(18, GPIO.OUT) - -# define default variables -> fan speed will be at max. if someting goes wrong with getting cpu temps -thermal = -99 -fanmode = 100 -i = 0 - -# function to get cpu temperature on linux and macOS and decide on fan speed. -def temp(thermal, fanmode): - thermal = int(round(float(subprocess.check_output("cat /sys/class/thermal/thermal_zone0/temp", shell=True).rstrip())/1000,2)) - if thermal <= 20: - fanmode = 0 - elif 20 < thermal <= 40: - fanmode = 25 - elif 40 < thermal <= 60: - fanmode = 50 - elif 60 < fanmode <= 70: - fanmode = 75 - elif 70 < fanmode: - fanmode = 100 - return thermal, fanmode - -# main loop -while True: - # get cpu temp - thermal, fanmode = temp(thermal, fanmode) - if fanmode == 0 or fanmode == 25: - GPIO.output(17, False) - GPIO.output(18, True) - elif fanmode == 50: - GPIO.output(17, True) - GPIO.output(18, False) - elif fanmode == 75: - GPIO.output(17, True) - GPIO.output(18, True) - else: - GPIO.output(17, False) - GPIO.output(18, False) - # cpu temp will be updated every 5 seconds - time.sleep(5) - i += 1 - # log cpu temp and fan speed - print("----- Run Nr.:", i, "\nCpu temperature measured: ", str(thermal) + "°C", "- Fanspeed: ", str(fanmode) + "%") \ No newline at end of file diff --git a/src/circuit.png b/src/circuit.png deleted file mode 100644 index 09dff3251ff618fe9d70c5f4c9f35ac2b503210c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26443 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