design update and socket time to 2s

This commit is contained in:
assada 2023-07-05 15:53:17 +03:00
parent 41d60e5019
commit 93c1596ba8
Signed by: assada
GPG Key ID: 8905E8CE5CC3000D
4 changed files with 266 additions and 89 deletions

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

40
testserver.py Normal file
View File

@ -0,0 +1,40 @@
import argparse
import pathlib
from aiohttp import web
file = open(pathlib.Path(__file__).parent.absolute() / 'webserver.html', 'r')
contecnt = file.read()
async def handle_index(request):
return web.Response(content_type='text/html', text=contecnt)
async def handle_ws(request):
ws = web.WebSocketResponse()
await ws.prepare(request)
request.app.ws_clients.append(ws)
async for _ in ws: # noqa: WPS328
pass
request.app.ws_clients.remove(ws)
return ws
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--bluetooth-mac', type=str, required=False, help='bluetooth MAC address of radiascan device')
parser.add_argument('--listen-host', type=str, required=False, default='0.0.0.0', help='listen host for webserver')
parser.add_argument('--listen-port', type=int, required=False, default=8080, help='listen port for webserver')
args = parser.parse_args()
app = web.Application()
app.ws_clients = []
app.add_routes(
[
web.get('/', handle_index)
],
)
web.run_app(app, host=args.listen_host, port=args.listen_port)

View File

@ -1,21 +1,39 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head>
<head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-GBWL3K2WMQ"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-GBWL3K2WMQ');
</script>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kyiv Radioactive!</title> <title>Київ неРадіоактивний! Проект dead.guru</title>
<meta name="description" content="неРадіоактивна ситуація в Києві в реальному часі. Спектр, події, радіаційний фон.">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script> <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script> <script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script>
<link rel="shortcut icon" href="https://kmr.gov.ua/sites/default/files/favicon-kyiv.ico"
type="image/vnd.microsoft.icon" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"
integrity="sha384-rOA1PnstxnOBLzCLMcre8ybwbTmemjzdNlILg8O7z1lUkLXozs4DHonlDtnE7fpc"
crossorigin="anonymous"></script>
<style> <style>
#app > div { #app>div {
margin: 5px auto; margin: 5px auto;
width: 80%; width: 80%;
text-align: center;
padding: 5px;
border: 1px #aaa dashed;
} }
#app fieldset { #app fieldset {
display: inline-block; display: inline-block;
border: 0; border: 0;
@ -23,83 +41,178 @@
margin-left: 20px; margin-left: 20px;
} }
#app > .notification { #app>.notification {
border: none; border: none;
} }
#map {
height: 180px;
width: 80%;
margin: 5px auto;
}
</style> </style>
</head> </head>
<body>
<body>
<section class="section"> <section class="section">
<div class="container"> <div class="container">
<h1 class="title"> <nav class="navbar is-transparent">
неРадіоактивна ситуація в Києві. <div class="navbar-brand">
</h1> <a class="navbar-item" href="https://kyiv.dead.guru">
<h2 class="subtitle">В реальному часі.</h2> <img src="/logo.png" alt="Bulma: a modern CSS framework based on Flexbox" width="112" height="28">
<div class="notification is-warning"> <div><span style="opacity: .9; color: #17914a; font-size: x-small;">не</span><span
Використовуйте ландшавтну орієнтацію екрану для зручного перегляду. Або планшет чи компьютер. style="font-size: x-small;">Радіоактивний!</span></div>
</a>
<div class="navbar-burger" data-target="navbarExampleTransparentExample">
<span></span>
<span></span>
<span></span>
</div> </div>
</div>
<div id="navbarExampleTransparentExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" href="https://www.saveecobot.com/radiation-maps">
Мапа датчиків
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#nav">
Корисні посилання
</a>
<div class="navbar-dropdown is-boxed">
<a class="navbar-item" href="https://www.windy.com/?50.321,30.504,9">
Мапа вітрів
</a>
<a class="navbar-item" href="https://bit.ly/3efbPmc">
Мапа укриттів
</a>
</a>
<a class="navbar-item" href="https://alerts.in.ua/">
Мапа тривог
</a>
<a class="navbar-item" href="https://bit.ly/3egOosD">
Поради від РНБО
</a>
<a class="navbar-item"
href="https://dmbk.kyivcity.gov.ua/content/dii-u-vypadku-zagrozy-vynyknennya-avarii-z-vykydom-rozlyvom-radioaktyvnyh-rechovyn.html">
Поради від департаменту муніципальної безпеки
</a>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item">
<div class="field is-grouped">
<p class="control">
<a class="bd-tw-button button" data-social-network="Twitter" data-social-action="tweet"
data-social-target="https://bulma.io" target="_blank"
href="https://twitter.com/intent/tweet?text=Київ неРадіоактивний! Перевірити можна на &amp;hashtags=КиївНеРадіоактивний&amp;url=https://kyiv.dead.guru">
<span class="icon">
<i class="fab fa-twitter"></i>
</span>
<span>
Tweet
</span>
</a>
</p>
</div>
</div>
</div>
</div>
</nav>
<div id="app"> <div id="app">
<div> <div v-if="al" class="notification is-warning">
<button class="delete" @click="al = !al"></button>
Використовуйте ландшафтну орієнтацію екрану для зручного перегляду. Ще краще — планшет чи комп'ютер.
</div>
<div style="text-align: center;">
<apexchart type="line" height="350" :options="ratesChartOptions" :series="rates_series"></apexchart> <apexchart type="line" height="350" :options="ratesChartOptions" :series="rates_series"></apexchart>
<button @click="rates_autoupdate = !rates_autoupdate" class="button">Автооновлення: {{ rates_autoupdate ? "ВКЛ" : "ВИКЛ" }}</button> <button @click="rates_autoupdate = !rates_autoupdate" class="button">Автооновлення: {{ rates_autoupdate ?
"ВКЛ" : "ВИКЛ" }}</button>
</div> </div>
<div class="notification"> <div class="notification">
<strong>До 0,3 мк3в/г - нормальний радіаційний фон.</strong> <strong>До 0,3 мк3в/г нормальний радіаційний фон.</strong>
<p> <p>
Швидкість дози в мікрозівертах на годину вказує, скільки мікрозівертів (одиниця виміру еквівалентної дози) радіації поглинається організмом або матеріалом за одну годину. Ця величина може використовуватися для оцінки рівня радіаційного впливу на людей, а також для моніторингу і контролю радіаційної безпеки в радіаційних зонах, ядерних установках або природному середовищі. Швидкість дози в мікрозівертах на годину вказує, скільки мікрозівертів (одиниця виміру еквівалентної дози)
радіації поглинається організмом або матеріалом за одну годину. Ця величина може використовуватися для
оцінки рівня радіаційного впливу на людей, а також для моніторингу і контролю радіаційної безпеки в
радіаційних зонах, ядерних установках або природному середовищі.
</p> </p>
<p> <p>
Наприклад, якщо швидкість дози становить 10 μSv/h, це означає, що організм або матеріал отримує дозу 10 мікрозівертів радіації за кожну годину. Це може вказувати на наявність джерела радіації в цьому місці або на необхідність заходів з радіаційного захисту. Наприклад, якщо швидкість дози становить 10 μSv/h, це означає, що організм або матеріал отримує дозу 10
мікрозівертів радіації за кожну годину. Це може вказувати на наявність джерела радіації в цьому місці або на
необхідність заходів з радіаційного захисту.
</p> </p>
</div> </div>
<div> <div style="text-align: center;">
<apexchart type="bar" height="350" :options="spectrumChartOptions" :series="spectrum_series"></apexchart> <apexchart type="bar" height="350" :options="spectrumChartOptions" :series="spectrum_series"></apexchart>
<div> <div>
<fieldset> <fieldset>
<label class="checkbox" for="spectrum_x_accum"><input type="checkbox" id="spectrum_x_accum" v-model="spectrum_accum"> Акумульований</label> <label class="checkbox" for="spectrum_x_accum"><input type="checkbox" id="spectrum_x_accum"
v-model="spectrum_accum"> Акумульований</label>
</fieldset> </fieldset>
<fieldset class="control"> <fieldset class="control">
<label class="radio" for="spectrum_x_channel"><input type="radio" id="spectrum_x_channel" v-bind:value="false" v-model="spectrum_energy"> Канал</label> <label class="radio" for="spectrum_x_channel"><input type="radio" id="spectrum_x_channel"
v-bind:value="false" v-model="spectrum_energy"> Канал</label>
<label class="radio" for="spectrum_x_energy"><input type="radio" id="spectrum_x_energy" v-bind:value="true" v-model="spectrum_energy"> Енергія</label> <label class="radio" for="spectrum_x_energy"><input type="radio" id="spectrum_x_energy"
v-bind:value="true" v-model="spectrum_energy"> Енергія</label>
</fieldset> </fieldset>
<fieldset class="control"> <fieldset class="control">
<label class="radio" for="spectrum_linear"><input type="radio" id="spectrum_linear" v-bind:value="false" v-model="spectrum_logarithmic"> Лінійне</label> <label class="radio" for="spectrum_linear"><input type="radio" id="spectrum_linear" v-bind:value="false"
v-model="spectrum_logarithmic"> Лінійне</label>
<label class="radio" for="spectrum_log"><input type="radio" id="spectrum_log" v-bind:value="true" v-model="spectrum_logarithmic"> Логарифмічне</label> <label class="radio" for="spectrum_log"><input type="radio" id="spectrum_log" v-bind:value="true"
v-model="spectrum_logarithmic"> Логарифмічне</label>
</fieldset> </fieldset>
</div> </div>
<button @click="updateSpectrum" class="button">Оновити спектр</button> <button @click="updateSpectrum" class="button">Оновити спектр</button>
</div> </div>
</div> </div>
<h2 class="subtitle">(Частина проекту <a href="https://dead.guru/">dead.guru</a>)</h2> <div id="map"></div>
</div> </div>
</section> </section>
<footer class="footer">
<div class="content has-text-centered">
<p>Важливо зазначити що явище радіоактивності не можна застосувати(вживати в контексті) до міста. Прочитати більше можна на <a href="https://uk.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B4%D1%96%D0%BE%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%96%D1%81%D1%82%D1%8C">Вікі</a>!</p>
<p>
Частина проекту <a href="https://dead.guru/"><strong>dead.guru</strong></a>. Код ліцензований
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. Вміст сайту
має ліцензію <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY NC SA 4.0</a>.
</p>
</div>
</footer>
<script> <script>
const common_options = { const common_options = {
chart: { chart: {
animations: {enabled: false}, animations: { enabled: false },
zoom: {autoScaleYaxis: true}, zoom: { autoScaleYaxis: true },
}, },
tooltip: {intersect: false}, tooltip: { intersect: false },
grid: {xaxis: {lines: {show: true}}}, grid: { xaxis: { lines: { show: true } } },
dataLabels: {enabled: false}, dataLabels: { enabled: false },
}; };
var app = new Vue({ var app = new Vue({
el: '#app', el: '#app',
components: { components: {
apexchart: VueApexCharts, apexchart: VueApexCharts,
}, },
data: function() { data: function () {
return { return {
ws: null, ws: null,
spectrum_duration: 0, spectrum_duration: 0,
rates_autoupdate: true, rates_autoupdate: true,
al: true,
rates_series: [], rates_series: [],
spectrum_accum: false, spectrum_accum: false,
spectrum_series: [], spectrum_series: [],
@ -108,11 +221,11 @@
spectrum_energy: true, spectrum_energy: true,
ratesChartOptions: { ratesChartOptions: {
...common_options, ...common_options,
title: {text: 'Активнсть подій (подій в секунду) і доза'}, title: { text: 'Активнсть подій (подій в секунду) і доза' },
xaxis: {type: 'datetime'}, xaxis: { type: 'datetime' },
yaxis: [ yaxis: [
{seriesName: 'Подій', title: {text: 'ПНС'}, labels: {formatter:(v) => v.toFixed(2) + ' ПНС'}}, { seriesName: 'Подій', title: { text: 'ПНС' }, labels: { formatter: (v) => v.toFixed(2) + ' ПНС' } },
{seriesName: 'Доза', title: {text: 'мк3в/г'}, labels: {formatter:(v) => v.toFixed(4) + ' мк3в/г'}, opposite: true}, { seriesName: 'Доза', title: { text: 'мк3в/г' }, labels: { formatter: (v) => v.toFixed(4) + ' мк3в/г' }, opposite: true },
], ],
}, },
}; };
@ -120,19 +233,22 @@
watch: { watch: {
spectrum_accum() { spectrum_accum() {
this.updateSpectrum(); this.updateSpectrum();
},
al(newAl) {
localStorage.al = newAl === true ? "ok!" : "no";
} }
}, },
computed: { computed: {
spectrumChartOptions() { spectrumChartOptions() {
const a0 = this.spectrum_coef[0], a1 = this.spectrum_coef[1], a2 = this.spectrum_coef[2]; const a0 = this.spectrum_coef[0], a1 = this.spectrum_coef[1], a2 = this.spectrum_coef[2];
const fmt = this.spectrum_energy ? ((c) => (a0 + a1*c + a2*c*c).toFixed(0)) : undefined; const fmt = this.spectrum_energy ? ((c) => (a0 + a1 * c + a2 * c * c).toFixed(0)) : undefined;
const title = this.spectrum_energy ? 'кеВ' : 'канал'; const title = this.spectrum_energy ? 'кеВ' : 'канал';
return{ return {
...common_options, ...common_options,
title: {text: `Спектр, ${this.spectrum_duration} секунд`}, title: { text: `Спектр, ${this.spectrum_duration} секунд` },
xaxis: {type: 'numeric', title: {text: title}, tickAmount: 25, labels: {formatter:fmt}}, xaxis: { type: 'numeric', title: { text: title }, tickAmount: 25, labels: { formatter: fmt } },
yaxis: {logarithmic: this.spectrum_logarithmic, decimalsInFloat: 0}, yaxis: { logarithmic: this.spectrum_logarithmic, decimalsInFloat: 0 },
plotOptions: {bar: {columnWidth: '95%'}}, plotOptions: { bar: { columnWidth: '95%' } },
}; };
}, },
}, },
@ -140,10 +256,30 @@
this.ws = new WebSocket('wss://' + window.location.host + '/ws') this.ws = new WebSocket('wss://' + window.location.host + '/ws')
this.ws.onmessage = this.onmessage; this.ws.onmessage = this.onmessage;
this.updateSpectrum(); this.updateSpectrum();
var map = L.map('map').setView([50.51847778550632, 30.508852993206236], 10);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 10,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
var circle = L.circle([50.51847778550632, 30.508852993206236], {
color: '#164299',
weight: 1,
opacity: 0.7,
fillColor: '#256FFF',
fillOpacity: 0.2,
radius: 5000
}).addTo(map);
circle.bindPopup("Зона виміру");
}, },
beforeDestroy: function() { beforeDestroy: function () {
this.ws.close(); this.ws.close();
}, },
mounted() {
if (localStorage.al) {
this.al = localStorage.al === "ok!";
}
},
methods: { methods: {
onmessage(ev) { onmessage(ev) {
if (!this.rates_autoupdate) { if (!this.rates_autoupdate) {
@ -155,10 +291,11 @@
updateSpectrum() { updateSpectrum() {
fetch(`/spectrum?accum=${this.spectrum_accum}`) fetch(`/spectrum?accum=${this.spectrum_accum}`)
.then(response => response.json()) .then(response => response.json())
.then(data => (this.spectrum_duration=data.duration, this.spectrum_coef=data.coef, this.spectrum_series=data.series)); .then(data => (this.spectrum_duration = data.duration, this.spectrum_coef = data.coef, this.spectrum_series = data.series));
}, },
}, },
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -78,7 +78,7 @@ async def process(app):
) )
print(f'Rates updated, sending to {len(app.ws_clients)} connected clients') print(f'Rates updated, sending to {len(app.ws_clients)} connected clients')
try: try:
await asyncio.gather(*[ws.send_str(jdata) for ws in app.ws_clients], asyncio.sleep(1.0)) await asyncio.gather(*[ws.send_str(jdata) for ws in app.ws_clients], asyncio.sleep(2.0))
except Exception as e: except Exception as e:
print(f'Error while sending to websocket: {e}') print(f'Error while sending to websocket: {e}')