From 154a52e30dc6a5f31379c6c128c63e9ba3659668 Mon Sep 17 00:00:00 2001 From: Untriex Programming Date: Fri, 9 Apr 2021 15:03:59 +0200 Subject: [PATCH] Human error prevention, sensor updates, etc. --- server/engine.py | 71 +++++++++++++++++++++- server/filesystem.json | 2 +- server/main.py | 132 +++++++++++++++++++++++++++++++---------- server/system.py | 37 ++++++++---- server/version.json | 5 +- 5 files changed, 198 insertions(+), 49 deletions(-) diff --git a/server/engine.py b/server/engine.py index 95d9566..83f986d 100644 --- a/server/engine.py +++ b/server/engine.py @@ -1,6 +1,8 @@ from datetime import datetime import json import requests +import os +import subprocess class Log: @@ -46,7 +48,7 @@ class Update: def __init__(self): with open("version.json", "r") as f: # loading settings version = json.load(f) - self.url = version["url"] + self.url = "https://raw.githubusercontent.com/UntriexTv/test_directory/main/ver.json" self.version = version["version"] self.id = version["id"] @@ -54,4 +56,69 @@ class Update: return json.loads(requests.get(self.url).text) def get_version(self): - return {"version": self.version, "id": self.id} \ No newline at end of file + return {"version": self.version, "id": self.id} + + +class Scan: + def __init__(self): + self.cache_exist = os.path.isdir("cache") + self.files_exist = os.path.isdir("files") + if os.path.isfile("update.zip"): + os.remove("update.zip") + self.filesystem_exist = os.path.isfile("filesystem.json") + self.settings_exist = os.path.isfile("settings.json") + self.version_exist = os.path.isfile("version.json") + self.errors = [] + self.state_list = { + "error": [], + "files": [], # 0 = does not exist, 1 = cant read, 2 = some values missing + "filesystem": [], + "settings": [], + "version": [], + "system": [] + } + + def check_to_go(self): + filesystem = "" + if self.cache_exist is False: + os.mkdir("cache") + if self.filesystem_exist is False: + self.state_list["error"].append("filesystem") + self.state_list["filesystem"].append(0) + self.errors.append("Filesystem is missing") + else: + try: + with open("filesystem.json", "r") as f: + filesystem = json.load(f) + except: + self.state_list["error"].append("filesystem") + self.state_list["filesystem"].append(1) + self.errors.append("Filesystem is corrupted") + else: + filesystem_keys = filesystem.keys() + for check in ["ID", "location", "description", "files"]: + if check not in filesystem_keys: + self.state_list["error"].append("filesystem") + self.state_list["filesystem"].append(2) + if self.files_exist is False: + self.state_list["error"].append("files") + self.state_list["files"].append(0) + self.errors.append("Files folder does not exists") + elif filesystem: + for file in dict(filesystem)["files"]: + if not os.path.isfile(f"""files/{dict(file)["name"]}{dict(file)["format"]}"""): + self.errors.append(f"""{dict(file)["name"]}{dict(file)["format"]} does not exists in file folder.""") + if "files" not in self.state_list["error"]: + self.state_list["error"].append("files") + self.state_list["files"].append(2) + if self.settings_exist is False: + self.state_list["error"].append("settings") + self.state_list["settings"].append(0) + if self.version_exist is False: + self.state_list["error"].append("version") + self.state_list["version"].append(0) + + + def fix_version(self): + subprocess.check_output(["python3", "system.py", "update"]) + diff --git a/server/filesystem.json b/server/filesystem.json index c64f093..bb938b7 100644 --- a/server/filesystem.json +++ b/server/filesystem.json @@ -1,5 +1,5 @@ { - "ID": "0", + "ID": 0, "location": "izba", "description": { "title": "legionrpi", diff --git a/server/main.py b/server/main.py index c60cdfa..3b93f78 100644 --- a/server/main.py +++ b/server/main.py @@ -1,20 +1,29 @@ import hashlib import json import os +import sys import threading import time import engine import requests import uuid +import subprocess from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from pydantic import BaseModel +check = engine.Scan() +check.check_to_go() +if check.state_list["error"]: + for error in check.errors: + print(error) + sys.exit() + with open("settings.json", "r") as f: # loading settings settings = json.load(f) -with open("filesystem.json", "r") as f: # loading settings +with open("filesystem.json", "r") as f: # loading filesystem filesystem = json.load(f) IP = settings["IP"] @@ -32,7 +41,6 @@ app.add_middleware( allow_methods=["*"], allow_headers=["*"], ) - log = engine.Log(settings["log"]) # init of LOG update = engine.Update() offline = [] @@ -44,12 +52,7 @@ time_to_heartbeat_offline = settings[ # json variables heartbeat_table = settings["heartbeat_table"] -sensors = { # List of "live" data like tempeature, etc. - "teplota": 24, - "vlhkosť": 25, - "počet ľudí": 10, - "doba čakania": 2 -} +sensors = {} messages = [] @@ -68,9 +71,14 @@ class ServerTable(BaseModel): # table of content for heartbeat request last_heartbeat: list +class Sensor(BaseModel): + name: str + value: str + + @app.get("/") def read_root(): - return {"Hello": "World"} + return "wikispot" @app.post("/heartbeat") @@ -107,19 +115,35 @@ def heartbeat(s_table: ServerTable, request: Request): return heartbeat_table, {"ID": ID, "file_system": filesystem, "location": location} -@app.get("/sensors") -def get_sensors(request: Request): +@app.get("/{IDx}/sensors") +def get_sensors(IDx: int, request: Request): + global sensors log.message(f"sensor data sent to {request.client.host}:{request.client.port}") log.debug(f"sensor data: {sensors}") - return sensors + if IDx == ID: + return sensors + else: + try: + r = requests.get(f"""http://{heartbeat_table["IP"][heartbeat_table["ID"].index(IDx)]}:8000/{IDx}/sensors""") + return r.json() + except Exception as error: + log.error(f"Sensor data download from {IDx} failed.\n ERROR: {error}") + return f"Sensor data download from {IDx} failed.\n ERROR: {error}" @app.get("/files/{IDx}/{file}") -def get_file(IDx: int, file: str): +def get_file(IDx: int, file: str, request: Request): + log.debug(f"""{request.client} requested {file} from {"this server" if IDx == ID else f"id {IDx}"}""") server_ip = heartbeat_table["IP"][heartbeat_table["ID"].index(IDx)] if IDx == ID: - return FileResponse(f"files/{file}") - elif IDx in heartbeat_table["ID"]: + if os.path.isfile(f"files/{file}"): + return FileResponse(f"files/{file}") + else: + return f"File {file} does not exist." + if IDx not in heartbeat_table["ID"]: + log.error(f"{request.client} tried to access id ({IDx}) that does not exist.") + return f"ERROR: {IDx} does not exist." + else: if os.path.isdir(f"cache/{IDx}"): if os.path.isfile(f"cache/{IDx}/{file}"): with open(f"cache/{IDx}/{file}", "rb") as compared_file: @@ -128,32 +152,44 @@ def get_file(IDx: int, file: str): m.update(line) rr = requests.get(f"""http://{server_ip}:8000/compare/{file}""") if rr.text.strip('"') != str(m.hexdigest()): - log.message(f"{file} on server {server_ip} is changed.") + log.warning(f"{file} on server {server_ip} is changed.") else: log.debug(f"returning cached file cache/{IDx}{file}") return FileResponse(f"cache/{IDx}/{file}") else: os.mkdir(f"cache/{IDx}") - log.message(f"downloading {file} from {server_ip}") r = requests.get(f"http://{server_ip}:8000/files/{IDx}/{file}") + if "does not exist" in r.text: + log.error(f"{request.client} tried to access file ({file}) on id {IDx} that does not exist.") + return f"ERROR: {file} does not exist." + log.message(f"Downloaded {file} from {server_ip}") with open(f"cache/{IDx}/{file}", "wb") as save: save.write(bytes(r.content)) return FileResponse(f"cache/{IDx}/{file}") -@app.post("/update") -def update_sensors(): - pass - # Todo Make option to upload "live data" manually to rpi +@app.post("/update_sensor") +def update_sensors(data: Sensor, request: Request): + global sensors + if data.name in sensors: + log.message(f"{request.client.host} updated sensor {data.name} with value {data.value}") + sensors[data.name] = data.value + else: + log.warning(f"{request.client} created new sensor.\n SENSOR: {data}") + sensors[data.name] = data.value + return f"Successfuly made" @app.get("/compare/{file}") def comparision(file: str): - with open(f"files/{file}", "rb") as compared_file: - m = hashlib.md5() - for line in compared_file: - m.update(line) - return m.hexdigest() + try: + with open(f"files/{file}", "rb") as compared_file: + m = hashlib.md5() + for line in compared_file: + m.update(line) + return m.hexdigest() + except FileNotFoundError: + return f"ERROR {file} does not exist" @app.get("/devices_list") @@ -161,12 +197,41 @@ def get_devices_list(): return [{"connected_id": ID}, *heartbeat_table["file_system"]] -@app.get("/admin/{command}") -def admin(command: str): +@app.get("/admin/get/{command}") +def admin_get(command: str): if command == "get_updates": return [update.get_version(), update.get_updates()] if "update-" in command: - os.system(f"""python3 system.py update -version {command.split("-")[1]}""") + state = [] + version = command.split("-")[1] + for rpi in heartbeat_table["IP"]: + if rpi != IP: + r = requests.get(f"""http://{rpi}:8000/admin/get/update_one-{version}""") + if r.text.strip('"').split("\\n")[0] == "SUCCESS": + log.message(f"{rpi} was updated to {version}") + else: + log.warning(f"""{rpi} failed to update. Manual update may be needed for proper working of network. + Response from server: {r.text}""") + state.append({rpi: r.text.strip('"').split("\\n")}) + # Todo Remove development comments + # subprocess.check_output(f"""python3 system.py update -version {version}""") + log.message(f"All devices in network should be updated to {version}") + state.append({IP: "updated"}) + return state + if "update_one-" in command: + state = subprocess.check_output(["python3", "system.py", "update", "-version", f"""{command.split("-")[1]}"""]) + log.warning(state.decode("utf-8")) + return state.decode("utf-8") + if command == "setting": + return settings + + +@app.post("admin/post/{command}") +def admin_post(command: str, data): + pass + + +# Todo upload of update file and settings @app.get("/messages/get") @@ -183,11 +248,16 @@ def get_messages(): def get_messages(m_sender: str = None, message: str = None): if m_sender and message: messages.append({"sender": m_sender, "message": message}) - return "successfull" + return "successful" else: return "Empty message/sender" +@app.get("/debug") +def debug_esp(): + return "test successful" + + def send_heartbeat(ip, id): global heartbeat_table log.message(f"""sending heartbeat to {ip}({"offline" if id in offline else "online"})""") @@ -238,6 +308,8 @@ def mainloop(): time.sleep(1) +print(f"""Starting WikiSpot V{update.get_version()["version"]}""") +print("GitHub: https://github.com/Tucan444/Mabasej_Team") thread_1 = threading.Thread(target=mainloop, daemon=True) thread_1.start() diff --git a/server/system.py b/server/system.py index d4281a0..4b51e74 100644 --- a/server/system.py +++ b/server/system.py @@ -6,16 +6,18 @@ import requests arguments = sys.argv arguments.remove(sys.argv[0]) +url = "https://raw.githubusercontent.com/UntriexTv/test_directory/main/ver.json" if len(arguments) == 0: sys.exit() command = arguments[0] if command in ["u", "update"]: - with open("version.json", "r") as f: # loading settings - version = json.load(f) - url = version["url"] - server_version = json.loads(requests.get(url).text) + try: + server_version = json.loads(requests.get(url).text) + except Exception as error: + print(f"CAN'T DOWNLOAD VERSION LIST. ERROR: {error}") + sys.exit() if "-version" in arguments: try: version_download = arguments[arguments.index("-version") + 1] @@ -27,23 +29,32 @@ if command in ["u", "update"]: sys.exit() else: + version_download = 0 + try: + with open("version.json", "r") as f: # loading settings + version = json.load(f) + except: + version = {"id": 0} for ver, data in enumerate(server_version.values()): - if data["id"] > version["id"]: + if data["id"] > version["id"] and ver > version_download: version_download = list(server_version.keys())[ver] + try: + with open("update.zip", "wb") as save: + save.write( + bytes(requests.get( + f"https://github.com/UntriexTv/test_directory/releases/download/{version_download}/update.zip").content)) + except Exception as error: + print(f"FAILED TO DOWNLOAD UPDATE. ERROR: {error}") + sys.exit() - with open("update.zip", "wb") as save: - save.write( - bytes(requests.get( - f"https://github.com/UntriexTv/test_directory/releases/download/{version_download}/update.zip").content)) - print("Download succefull") - print("Extracting update") if not os.path.isdir("update"): os.mkdir("update") with zipfile.ZipFile("update.zip", "r") as zip_ref: - zip_ref.extractall("") + zip_ref.extractall("update") os.rmdir("update") os.remove("update.zip") - print(f"update to {version_download} was succefull.") + print("SUCCESS") + print(f"""Update from version {version["version"]} to {version_download} was sucesfull""") if command == "clean": if arguments[1] == "all": diff --git a/server/version.json b/server/version.json index e18780f..4be0caf 100644 --- a/server/version.json +++ b/server/version.json @@ -1,5 +1,4 @@ { - "version": "0.6", - "id": 4, - "url": "https://raw.githubusercontent.com/UntriexTv/test_directory/main/ver.json" + "version": "0.9", + "id": 6 }