strated on basic framework for dashboard in web_monitor

This commit is contained in:
UpstreamData
2022-03-04 11:24:06 -07:00
parent 42f5146632
commit 87b8de9029
4 changed files with 109 additions and 9 deletions

View File

@@ -4,6 +4,7 @@ import datetime
import os import os
import asyncio import asyncio
import uvicorn import uvicorn
import websockets.exceptions
from fastapi import FastAPI from fastapi import FastAPI
from fastapi import Request from fastapi import Request
from fastapi import WebSocket, WebSocketDisconnect from fastapi import WebSocket, WebSocketDisconnect
@@ -19,6 +20,11 @@ templates = Jinja2Templates(directory="templates")
@app.get("/") @app.get("/")
def index():
return dashboard()
@app.get("/dashboard")
def dashboard(request: Request): def dashboard(request: Request):
return templates.TemplateResponse("index.html", { return templates.TemplateResponse("index.html", {
"request": request, "request": request,
@@ -26,6 +32,57 @@ def dashboard(request: Request):
}) })
@app.websocket("/dashboard/ws")
async def dashboard_websocket(websocket: WebSocket):
await websocket.accept()
try:
while True:
miners = get_current_miner_list()
all_miner_data = []
data_gen = asyncio.as_completed([get_miner_data_dashboard(miner) for miner in miners])
for all_data in data_gen:
data_point = await all_data
all_miner_data.append(data_point)
all_miner_data.sort(key=lambda x: x["ip"])
try:
await websocket.send_json({"datetime": datetime.datetime.now().isoformat(),
"miners": all_miner_data})
except websockets.exceptions.ConnectionClosedOK:
print("disconnected")
await asyncio.sleep(5)
except WebSocketDisconnect:
print("Websocket disconnected.")
pass
async def get_miner_data_dashboard(miner_ip):
try:
miner = await asyncio.wait_for(miner_factory.get_miner(miner_ip), 5)
miner_summary = await asyncio.wait_for(miner.api.summary(), 5)
if miner_summary:
if 'MHS av' in miner_summary['SUMMARY'][0].keys():
hashrate = format(
round(miner_summary['SUMMARY'][0]['MHS av'] / 1000000,
2), ".2f")
elif 'GHS av' in miner_summary['SUMMARY'][0].keys():
hashrate = format(
round(miner_summary['SUMMARY'][0]['GHS av'] / 1000, 2),
".2f")
else:
hashrate = 0
else:
hashrate = 0
return {"ip": str(miner.ip), "hashrate": hashrate}
except asyncio.exceptions.TimeoutError:
return {"ip": miner_ip, "error": "The miner is not responding."}
except KeyError:
return {"ip": miner_ip, "error": "The miner returned unusable/unsupported data."}
@app.get("/scan") @app.get("/scan")
def scan(request: Request): def scan(request: Request):
return templates.TemplateResponse("scan.html", { return templates.TemplateResponse("scan.html", {
@@ -35,7 +92,7 @@ def scan(request: Request):
@app.get("/miner") @app.get("/miner")
def miner(request: Request, miner_ip): def miner(_request: Request, _miner_ip):
return get_miner return get_miner
@@ -71,7 +128,6 @@ async def miner_websocket(websocket: WebSocket, miner_ip):
if item in miner_stats["STATS"][1].keys(): if item in miner_stats["STATS"][1].keys():
miner_fans["FANS"].append({"RPM": miner_stats["STATS"][1][item]}) miner_fans["FANS"].append({"RPM": miner_stats["STATS"][1][item]})
if miner_summary: if miner_summary:
if 'MHS av' in miner_summary['SUMMARY'][0].keys(): if 'MHS av' in miner_summary['SUMMARY'][0].keys():
hashrate = format( hashrate = format(
@@ -95,7 +151,6 @@ async def miner_websocket(websocket: WebSocket, miner_ip):
while len(fan_speeds) < 5: while len(fan_speeds) < 5:
fan_speeds.append(0) fan_speeds.append(0)
data = {"hashrate": hashrate, data = {"hashrate": hashrate,
"fans": fan_speeds, "fans": fan_speeds,
"datetime": datetime.datetime.now().isoformat()} "datetime": datetime.datetime.now().isoformat()}
@@ -107,7 +162,7 @@ async def miner_websocket(websocket: WebSocket, miner_ip):
await asyncio.sleep(.5) await asyncio.sleep(.5)
except KeyError as e: except KeyError as e:
print(e) print(e)
data = {"error": "The miner returned incorrect data."} data = {"error": "The miner returned unusable/unsupported data."}
await websocket.send_json(data) await websocket.send_json(data)
await asyncio.sleep(.5) await asyncio.sleep(.5)
except WebSocketDisconnect: except WebSocketDisconnect:

View File

@@ -151,6 +151,7 @@ main {
} }
.nav-pills .nav-link.active { .nav-pills .nav-link.active {
color: #212529;
background-image: linear-gradient(180deg, #D0368A 0%, #708AD4 99%); background-image: linear-gradient(180deg, #D0368A 0%, #708AD4 99%);
} }

View File

@@ -1,4 +1,48 @@
{% extends 'navbar.html'%} {% extends 'navbar.html'%}
{% block content %} {% block content %}
<p>Hi</p> <div id="data"></div>
<div class="col" id="errors">
</div>
<script>
var ws = new WebSocket("ws://localhost:80/dashboard/ws");
let all_data = []
let all_labels = []
ws.onmessage = function(event) {
var new_data = JSON.parse(event.data)
if (new_data.hasOwnProperty("error")) {
var err_container = document.getElementById("errorContainer")
var err_code = document.getElementById("errorCode")
err_code.innerHTML = new_data['error']
err_container.classList.remove("invisible")
} else {
div = document.getElementById("data")
errors = document.getElementById("errors")
div.innerHTML = ""
for (i = 0; i< new_data["miners"].length; i++) {
if (new_data["miners"][i].hasOwnProperty("error")) {
if (!document.getElementById(new_data["miners"][i]["ip"] + "_error")) {
errors.innerHTML += "<div id='" + new_data["miners"][i]["ip"] + "_error" +
"' class='d-flex align-items-center p-1 alert alert-danger'><strong class='p-0 m-0'>" +
new_data["miners"][i]["ip"] + ": " +
new_data["miners"][i]["error"] +
"</strong><div class='spinner-border spinner-border-sm ms-auto'></div></div>"
}
} else {
if (document.getElementById(new_data["miners"][i]["ip"] + "_error")) {
document.getElementById(new_data["miners"][i]["ip"] + "_error").remove()
}
div.innerHTML += new_data["miners"][i]["ip"] + ": " + new_data["miners"][i]["hashrate"] + "\n"
}
}
};
};
</script>
{% endblock content %} {% endblock content %}

View File

@@ -51,27 +51,27 @@
<div class="position-sticky pt-3"> <div class="position-sticky pt-3">
<ul class="nav nav-pills flex-column"> <ul class="nav nav-pills flex-column">
<li class="nav-item mb-1 mx-2"> <li class="nav-item mb-1 mx-2">
<a href="{{url_for('dashboard')}}" class="nav-link text-white {% if request.path == '/' %}active{% endif %}"> <a href="{{url_for('dashboard')}}" class="nav-link {% if request.path == '/dashboard' %}active{% else %}text-white{% endif %}">
<svg class="bi me-2" width="16" height="16"><use xlink:href="#dashboard"></use></svg> <svg class="bi me-2" width="16" height="16"><use xlink:href="#dashboard"></use></svg>
Dashboard Dashboard
</a> </a>
</li> </li>
<li class="nav-item mb-1 mx-2"> <li class="nav-item mb-1 mx-2">
<a href="" class="nav-link text-white {% if request.path == '/scan' or request.path.split('/')[1] == 'miner' %}active{% endif %}" data-bs-toggle="collapse" data-bs-target="#miners-collapse" aria-expanded="false"> <a href="" class="nav-link {% if request.path == '/scan' or request.path.split('/')[1] == 'miner' %}active{% else %}text-white{% endif %}" data-bs-toggle="collapse" data-bs-target="#miners-collapse" aria-expanded="false">
<svg class="bi me-2" width="16" height="16"><use xlink:href="#miners"></use></svg> <svg class="bi me-2" width="16" height="16"><use xlink:href="#miners"></use></svg>
Miners Miners
</a> </a>
<div class="collapse mt-1" id="miners-collapse" style=""> <div class="collapse mt-1" id="miners-collapse" style="">
<ul class="btn-toggle-nav overflow-auto list-unstyled fw-normal pb-1 small"> <ul class="btn-toggle-nav overflow-auto list-unstyled fw-normal pb-1 small">
<li> <li>
<a href="{{url_for('scan')}}" class="nav-link text-white {% if request.path == '/scan' %}active{% endif %}"> <a href="{{url_for('scan')}}" class="nav-link {% if request.path == '/scan' %}active{% else %}text-white{% endif %}">
<svg class="bi me-2 mt-1" width="16" height="16"><use xlink:href="#scan"></use></svg> <svg class="bi me-2 mt-1" width="16" height="16"><use xlink:href="#scan"></use></svg>
Add Miners Add Miners
</a> </a>
</li> </li>
{% for miner in cur_miners %} {% for miner in cur_miners %}
<li> <li>
<a href="{{url_for('miner')}}/{{miner}}" class="text-white nav-link {% if request.path == '/miner/' + miner %}active{% endif %}"> <a href="{{url_for('miner')}}/{{miner}}" class="nav-link {% if request.path == '/miner/' + miner %}active{% else %}text-white{% endif %}">
<svg class="bi me-2 mt-1" width="16" height="16"><use xlink:href="#miner"></use></svg> <svg class="bi me-2 mt-1" width="16" height="16"><use xlink:href="#miner"></use></svg>
{{miner}} {{miner}}
</a> </a>