strated on basic framework for dashboard in web_monitor
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user