Online updates/save heartbeat table etc.

News:
- Online updates from test github repository
- Removing offline clients after selected period of time
- Saving heartbeat table (can be disabled)
Added system.py it is handeling updates and cleaning of logs/heartbeat table. In the future should be able to fix errors in case program crashes.
This commit is contained in:
Untriex Programming 2021-03-21 12:52:55 +01:00
parent 55fcf7d4d4
commit 3a34e50432
9 changed files with 188 additions and 129 deletions

6
server/.idea/vcs.xml Normal file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="7d87058b-045e-4cef-a58c-742b2c4128db" name="Default Changelist" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Merge.Settings">
<option name="BRANCH" value="Development" />
</component>
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$/.." value="Development" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
<component name="ProjectId" id="1ptaObqar0IjON5s6ejh3akwf0j" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../../Mabasej_work_server" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="7d87058b-045e-4cef-a58c-742b2c4128db" name="Default Changelist" comment="" />
<created>1616004808992</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1616004808992</updated>
</task>
<servers />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
<option name="oldMeFiltersMigrated" value="true" />
</component>
</project>

@ -1,4 +1,6 @@
from datetime import datetime from datetime import datetime
import json
import requests
class Log: class Log:
@ -38,3 +40,18 @@ class Log:
def debug(self, debug): def debug(self, debug):
if self.debug_e: if self.debug_e:
print(f"{datetime.now()} -> DEBUG: {debug}") print(f"{datetime.now()} -> DEBUG: {debug}")
class Update:
def __init__(self):
with open("version.json", "r") as f: # loading settings
version = json.load(f)
self.url = version["url"]
self.version = version["version"]
self.id = version["id"]
def get_updates(self):
return json.loads(requests.get(self.url).text)
def get_version(self):
return {"version": self.version, "id": self.id}

@ -1,5 +1,5 @@
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.responses import FileResponse from fastapi.responses import FileResponse, HTMLResponse
from pydantic import BaseModel from pydantic import BaseModel
import engine import engine
import requests import requests
@ -18,10 +18,13 @@ with open("filesystem.json", "r") as f: # loading settings
IP = settings["IP"] IP = settings["IP"]
ID = settings["ID"] ID = settings["ID"]
location = settings["location"] location = settings["location"]
time_to_save = settings["time_to_save"]
app = FastAPI() # init of FastAPI app = FastAPI() # init of FastAPI
log = engine.Log(settings["log"]) # init of LOG log = engine.Log(settings["log"]) # init of LOG
update = engine.Update()
offline = [] offline = []
save_time = time.time()
time_to_heartbeat = settings["time_to_heartbeat"] # Raspberry will be requesting heartbeat every __ seconds time_to_heartbeat = settings["time_to_heartbeat"] # Raspberry will be requesting heartbeat every __ seconds
time_to_heartbeat_offline = settings[ time_to_heartbeat_offline = settings[
@ -141,6 +144,14 @@ def get_devices_list():
return heartbeat_table["file_system"] return heartbeat_table["file_system"]
@app.get("/admin/{command}")
def admin(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]}""")
def send_heartbeat(ip, id): def send_heartbeat(ip, id):
global heartbeat_table global heartbeat_table
log.message(f"""sending heartbeat to {ip}({"offline" if id in offline else "online"})""") log.message(f"""sending heartbeat to {ip}({"offline" if id in offline else "online"})""")
@ -150,6 +161,7 @@ def send_heartbeat(ip, id):
def mainloop(): def mainloop():
global save_time
while True: while True:
for device_number, device_ID in enumerate(heartbeat_table["ID"]): for device_number, device_ID in enumerate(heartbeat_table["ID"]):
if device_ID != ID: if device_ID != ID:
@ -161,21 +173,36 @@ def mainloop():
log.warning(f"""{heartbeat_table["IP"][device_number]} disconnected/is not available""") log.warning(f"""{heartbeat_table["IP"][device_number]} disconnected/is not available""")
offline.append(heartbeat_table["ID"][device_number]) offline.append(heartbeat_table["ID"][device_number])
heartbeat_table["last_heartbeat"][int(device_number)] = int(time_to_heartbeat_offline) heartbeat_table["last_heartbeat"][int(device_number)] = int(time_to_heartbeat_offline)
else:
offline.remove(heartbeat_table["ID"][device_number])
log.message(f"""Removing {device_ID} because of long inactivity.""")
del heartbeat_table["ID"][device_number]
del heartbeat_table["IP"][device_number]
del heartbeat_table["location"][device_number]
del heartbeat_table["file_system"][device_number]
del heartbeat_table["last_heartbeat"][device_number]
else: else:
if heartbeat_table["ID"][device_number] in offline: if heartbeat_table["ID"][device_number] in offline:
offline.remove(heartbeat_table["ID"][device_number]) offline.remove(heartbeat_table["ID"][device_number])
log.message(f"""{heartbeat_table["IP"][device_number]} gone online""") log.message(f"""{heartbeat_table["IP"][device_number]} gone online""")
heartbeat_table["last_heartbeat"][int(device_number)] = int(time_to_heartbeat) + 5 heartbeat_table["last_heartbeat"][int(device_number)] = int(time_to_heartbeat) + 5
try:
log.debug(f"""{device_ID} : time to heartbeat : {heartbeat_table["last_heartbeat"][device_number]}""") log.debug(f"""{device_ID} : time to heartbeat : {heartbeat_table["last_heartbeat"][device_number]}""")
heartbeat_table["last_heartbeat"][device_number] -= 1 heartbeat_table["last_heartbeat"][device_number] -= 1
except IndexError:
pass
if time.time() - time_to_save > save_time and settings["save_table"]:
save_time = time.time()
log.message("Saving heartbeat table.")
log.debug(f"Saving heartbeat table: {heartbeat_table}")
settings["heartbeat_table"] = heartbeat_table
with open("settings.json", "w") as file:
json.dump(settings, file, indent=2)
time.sleep(1) time.sleep(1)
thread_1 = threading.Thread(target=mainloop, daemon=True) thread_1 = threading.Thread(target=mainloop, daemon=True)
thread_1.start() thread_1.start()
# Todo in next release: disconnect offline client after set time
# Todo send to mobile
# Todo new filesystem handeling # Todo new filesystem handeling
# Todo implement update system
# Todo settings for easy adding/editing files/id/text # Todo settings for easy adding/editing files/id/text

@ -1,12 +0,0 @@
import tkinter
import json
height = 750
width = 1200
with open("settings.json", "r") as file:
settings = json.load(file)
with open("settings.json", "r") as file:
filesystem = json.load(file)
canvas = tkinter.Canvas(height=height, width=width)
canvas.pack()
canvas.mainloop()

72
server/system.py Normal file

@ -0,0 +1,72 @@
import sys
import os
import json
import zipfile
import requests
arguments = sys.argv
arguments.remove(sys.argv[0])
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)
if "-version" in arguments:
try:
version_download = arguments[arguments.index("-version") + 1]
except IndexError:
print("Version argument is empty.")
sys.exit()
if version_download not in list(server_version.keys()):
print("Version not found.")
sys.exit()
else:
for ver, data in enumerate(server_version.values()):
if data["id"] > version["id"]:
version_download = list(server_version.keys())[ver]
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("")
os.rmdir("update")
os.remove("update.zip")
print(f"update to {version_download} was succefull.")
if command == "clean":
if arguments[1] == "all":
open("log.txt", "w").close()
with open("settings.json", "r") as file:
settings = json.load(file)
for line in settings["heartbeat_table"]:
settings["heartbeat_table"][line] = []
with open("settings.json", "w") as file:
json.dump(settings, file, indent=2)
if arguments[1] == "log":
open("log.txt", "w").close()
if arguments[1] == "heartbeat_table":
with open("settings.json", "r") as file:
settings = json.load(file)
for line in settings["heartbeat_table"]:
settings["heartbeat_table"][line] = []
with open("settings.json", "w") as file:
json.dump(settings, file, indent=2)

@ -1,47 +0,0 @@
{
"connected_id": 1,
"1": {
"ID": 1,
"location": "GPS",
"descrpition": {
"title": "nazov rpi ako nazov",
"description_s": "krátky popis, ktorý bude zobrazený iba v náhladovom okne",
"description_l": "dlhší popis zariadenia, ktorý bude zobrazený po otvorení",
"photo_s": "mala_fotka.png",
"photo_b": "velka_fotka.png"
},
"files": [
{
"name": "prehliadky",
"format": ".pdf",
"description": "tento súbor obsahuje prehliadky"
}, {
"name": "prehliadky",
"format": ".pdf",
"description": "tento súbor obsahuje prehliadky"
}
]
},
"2": {
"ID": 2,
"location": "GPS",
"descrpition": {
"title": "nazov rpi ako nazov",
"description_s": "krátky popis, ktorý bude zobrazený iba v náhladovom okne",
"description_l": "dlhší popis zariadenia, ktorý bude zobrazený po otvorení",
"photo_s": "mala_fotka.png",
"photo_b": "velka fotka.png"
},
"files": [
{
"name": "prehliadky",
"format": ".pdf",
"description": "tento súbor obsahuje prehliadky"
}, {
"name": "prehliadky",
"format": ".pdf",
"description": "tento súbor obsahuje prehliadky"
}
]
}
}

@ -1,60 +0,0 @@
import curses
menu = ['Home', 'Play', 'Scoreboard', 'Exit']
def print_menu(stdscr, selected_row_idx):
stdscr.clear()
h, w = stdscr.getmaxyx()
for idx, row in enumerate(menu):
x = w//2 - len(row)//2
y = h//2 - len(menu)//2 + idx
if idx == selected_row_idx:
stdscr.attron(curses.color_pair(1))
stdscr.addstr(y, x, row)
stdscr.attroff(curses.color_pair(1))
else:
stdscr.addstr(y, x, row)
stdscr.refresh()
def print_center(stdscr, text):
stdscr.clear()
h, w = stdscr.getmaxyx()
x = w//2 - len(text)//2
y = h//2
stdscr.addstr(y, x, text)
stdscr.refresh()
def main(stdscr):
# turn off cursor blinking
curses.curs_set(0)
# color scheme for selected row
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
# specify the current selected row
current_row = 0
# print the menu
print_menu(stdscr, current_row)
while 1:
key = stdscr.getch()
if key == curses.KEY_UP and current_row > 0:
current_row -= 1
elif key == curses.KEY_DOWN and current_row < len(menu)-1:
current_row += 1
elif key == curses.KEY_ENTER or key in [10, 13]:
print_center(stdscr, "You selected '{}'".format(menu[current_row]))
stdscr.getch()
# if user selected last row, exit the program
if current_row == len(menu)-1:
break
print_menu(stdscr, current_row)
curses.wrapper(main)

@ -1,4 +1,5 @@
{ {
"version": "0.1", "version": "0.5",
"type": "Alpha" "id": 3,
"url": "https://raw.githubusercontent.com/UntriexTv/test_directory/main/ver.json"
} }