Merge pull request #504 from DJ2LS/ls-gui-single-pager

GUI Adjustments
This commit is contained in:
DJ2LS 2023-10-28 20:12:07 +02:00 committed by GitHub
commit 1bb18e1e98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 793 additions and 415 deletions

BIN
gui/public/dj2ls.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
gui/public/ei7ig.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
gui/public/kt4wo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

BIN
gui/public/vk5dgr.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View file

@ -45,10 +45,14 @@ function chatSelected(callsign) {
}
</script>
<template>
<div class="list-group m-0 p-0" id="chat-list-tab" role="chat-tablist">
<div
class="list-group bg-body-tertiary m-0 p-1"
id="chat-list-tab"
role="chat-tablist"
>
<template v-for="(item, key) in chat.callsign_list" :key="item.dxcallsign">
<a
class="list-group-item list-group-item-action border-0 border-bottom rounded-0"
class="list-group-item list-group-item-action list-group-item-dark rounded-2 border-0 mb-2"
:class="{ active: key == 0 }"
:id="`list-chat-list-${item}`"
data-bs-toggle="list"
@ -59,7 +63,7 @@ function chatSelected(callsign) {
>
<div class="row">
<div class="col-9">
{{ item }}
<strong>{{ item }}</strong>
<span
class="badge rounded-pill bg-danger"
v-if="getNewMessagesByDXCallsign(item)[1] > 0"

View file

@ -43,7 +43,8 @@
aria-valuemin="0"
aria-valuemax="100"
>
{{ message.percent }} % with {{ message.bytesperminute }} bpm
{{ message.percent }} % with {{ message.bytesperminute }} bpm (
{{ message.status }} )
</div>
</div>
</div>

View file

@ -33,7 +33,7 @@
<!-- message area -->
<div :class="messageWidthClass">
<div class="card bg-primary text-white">
<div class="card bg-secondary text-white">
<div class="card-header" v-if="getFileContent['filesize'] !== 0">
<p class="card-text">
{{ getFileContent["filename"] }} |
@ -46,15 +46,23 @@
<p class="card-text">{{ message.msg }}</p>
</div>
<div class="card-footer p-0 bg-primary border-top-0">
<div class="card-footer p-0 bg-secondary border-top-0">
<p class="text p-0 m-0 me-1 text-end">{{ getDateTime }}</p>
<!-- Display formatted timestamp in card-footer -->
</div>
<div class="card-footer p-0 border-top-0" v-if="message.percent < 100">
<div
class="card-footer p-0 border-top-0"
v-if="message.percent < 100 || message.status === 'failed'"
>
<div
class="progress bg-secondary rounded-0 rounded-bottom"
class="progress rounded-0 rounded-bottom"
:style="{ height: '10px' }"
v-bind:class="{
'bg-danger': message.status == 'failed',
'bg-primary': message.status == 'transmitting',
'bg-secondary': message.status == 'transmitted',
}"
>
<div
class="progress-bar progress-bar-striped overflow-visible"
@ -64,7 +72,8 @@
aria-valuemin="0"
aria-valuemax="100"
>
{{ message.percent }} % with {{ message.bytesperminute }} bpm
{{ message.percent }} % with {{ message.bytesperminute }} bpm (
{{ message.status }} )
</div>
</div>
</div>

View file

@ -0,0 +1,227 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { setActivePinia } from "pinia";
import pinia from "../store/index";
setActivePinia(pinia);
import { useStateStore } from "../store/stateStore.js";
const state = useStateStore(pinia);
const version = import.meta.env.PACKAGE_VERSION;
function openWebExternal(url) {
open(url);
}
const cards = ref([
{
titleName: "Simon",
titleCall: "DJ2LS",
role: "Founder & Core developer",
imgSrc: "dj2ls.png",
},
{
titleName: "Alan",
titleCall: "N1QM",
role: "developer",
imgSrc: "person-fill.svg",
},
{
titleName: "Stefan",
titleCall: "DK5SM",
role: "tester",
imgSrc: "person-fill.svg",
},
{
titleName: "Wolfgang",
titleCall: "DL4IAZ",
role: "supporter",
imgSrc: "person-fill.svg",
},
{
titleName: "David",
titleCall: "VK5DGR",
role: "codec2 founder",
imgSrc: "vk5dgr.jpeg",
},
{
titleName: "John",
titleCall: "EI7IG",
role: "tester",
imgSrc: "ei7ig.jpeg",
},
{
titleName: "Paul",
titleCall: "N2KIQ",
role: "developer",
imgSrc: "person-fill.svg",
},
{
titleName: "Trip",
titleCall: "KT4WO",
role: "tester",
imgSrc: "kt4wo.png",
},
{
titleName: "Manuel",
titleCall: "DF7MH",
role: "tester",
imgSrc: "person-fill.svg",
},
{
titleName: "Darren",
titleCall: "G0HWW",
role: "tester",
imgSrc: "person-fill.svg",
},
{
titleName: "Kai",
titleCall: "LA3QMA",
role: "developer",
imgSrc: "person-fill.svg",
},
]);
// Shuffle cards
function shuffleCards() {
cards.value = cards.value.sort(() => Math.random() - 0.5);
}
onMounted(shuffleCards);
</script>
<template>
<h3 class="m-2">
<span class="badge bg-secondary">FreeDATA: {{ version }}</span>
<span class="ms-2 badge bg-secondary"
>Modem: {{ state.modem_version }}</span
>
</h3>
<div class="container-fluid">
<div class="row mt-2">
<hr />
<h6>Important URLs</h6>
<div
class="btn-toolbar mx-auto"
role="toolbar"
aria-label="Toolbar with button groups"
>
<div class="btn-group">
<button
class="btn btn-sm bi bi-geo-alt btn-secondary me-2"
id="openExplorer"
type="button"
data-bs-placement="bottom"
@click="openWebExternal('https://explorer.freedata.app')"
>
Explorer map
</button>
</div>
<div class="btn-group">
<button
class="btn btn-sm btn-secondary me-2 bi bi-graph-up"
id="btnStats"
type="button"
data-bs-placement="bottom"
@click="openWebExternal('https://statistics.freedata.app/')"
>
Statistics
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-bookmarks me-2"
id="fdWww"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="FreeDATA website"
role="button"
@click="openWebExternal('https://freedata.app')"
>
Website
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-github me-2"
id="ghUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Github"
role="button"
@click="openWebExternal('https://github.com/dj2ls/freedata')"
>
Github
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-wikipedia me-2"
id="wikiUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Wiki"
role="button"
@click="openWebExternal('https://wiki.freedata.app')"
>
Wiki
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-discord"
id="discordUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Discord"
role="button"
@click="openWebExternal('https://discord.freedata.app')"
>
Discord
</button>
</div>
</div>
</div>
<div class="row mt-5">
<hr />
<h6>Special thanks to</h6>
</div>
<div class="d-flex flex-nowrap overflow-x-auto vh-100">
<div class="row row-cols-1 row-cols-md-6 g-4 h-100">
<div class="d-inline-block" v-for="card in cards" :key="card.titleName">
<div class="col">
<div
class="card border-dark mb-3 ms-1 me-1"
style="max-width: 15rem"
>
<img :src="card.imgSrc" class="card-img-top grayscale" />
<div class="card-body">
<p class="card-text text-center">{{ card.role }}</p>
</div>
<div class="card-footer text-body-secondary text-center">
<strong>{{ card.titleCall }}</strong>
</div>
<div class="card-footer text-body-secondary text-center">
<strong>{{ card.titleName }}</strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style>
.grayscale {
filter: grayscale(100%);
transition: filter 0.3s ease-in-out;
}
.grayscale:hover {
filter: grayscale(0);
}
</style>

View file

@ -3,9 +3,6 @@ import { setActivePinia } from "pinia";
import pinia from "../store/index";
setActivePinia(pinia);
import { useStateStore } from "../store/stateStore.js";
const state = useStateStore(pinia);
import main_modals from "./main_modals.vue";
import main_top_navbar from "./main_top_navbar.vue";
import main_audio from "./main_audio.vue";
@ -22,18 +19,14 @@ import main_active_heard_stations from "./main_active_heard_stations.vue";
import main_active_audio_level from "./main_active_audio_level.vue";
import chat from "./chat.vue";
import infoScreen from "./infoScreen.vue";
import { stopTransmission } from "../js/sock.js";
const version = import.meta.env.PACKAGE_VERSION;
function stopAllTransmissions() {
console.log("stopping transmissions");
stopTransmission();
}
function openWebExternal(url) {
open(url);
}
</script>
<template>
@ -63,7 +56,7 @@ function openWebExternal(url) {
style="margin-top: 100px"
>
<a
class="list-group-item list-group-item-action active"
class="list-group-item list-group-item-dark list-group-item-action border-0 rounded-3 mb-2 active"
id="list-modem-list"
data-bs-toggle="list"
href="#list-modem"
@ -73,7 +66,7 @@ function openWebExternal(url) {
><i class="bi bi-house-door-fill h3"></i
></a>
<a
class="list-group-item list-group-item-action"
class="list-group-item list-group-item-dark list-group-item-action border-0 rounded-3 mb-2"
id="list-chat-list"
data-bs-toggle="list"
href="#list-chat"
@ -84,7 +77,7 @@ function openWebExternal(url) {
></a>
<a
class="list-group-item list-group-item-action d-none"
class="list-group-item list-group-item-dark list-group-item-action d-none border-0 rounded-3 mb-2"
id="list-mesh-list"
data-bs-toggle="list"
href="#list-mesh"
@ -94,7 +87,7 @@ function openWebExternal(url) {
></a>
<a
class="list-group-item list-group-item-action mt-2 border"
class="list-group-item list-group-item-dark list-group-item-action border border-0 rounded-3 mb-2"
id="list-info-list"
data-bs-toggle="list"
href="#list-info"
@ -105,7 +98,7 @@ function openWebExternal(url) {
></a>
<a
class="list-group-item list-group-item-action d-none"
class="list-group-item list-group-item-dark list-group-item-action d-none border-0 rounded-3 mb-2"
id="list-logger-list"
data-bs-toggle="list"
href="#list-logger"
@ -115,7 +108,7 @@ function openWebExternal(url) {
></a>
<a
class="list-group-item list-group-item-action rounded-bottom"
class="list-group-item list-group-item-dark list-group-item-action border-0 rounded-3 mb-2"
id="list-settings-list"
data-bs-toggle="list"
href="#list-settings"
@ -354,109 +347,7 @@ function openWebExternal(url) {
role="tabpanel"
aria-labelledby="list-info-list"
>
<h1 class="modal-title fs-5" id="aboutModalLabel">
FreeDATA - {{ version }}
</h1>
<h4 class="fs-5">modem version - {{ state.modem_version }}</h4>
<div class="container-fluid">
<div class="row mt-2">
<div
class="btn-toolbar mx-auto"
role="toolbar"
aria-label="Toolbar with button groups"
>
<div class="btn-group">
<button
class="btn btn-sm bi bi-geo-alt btn-secondary me-2"
id="openExplorer"
type="button"
data-bs-placement="bottom"
@click="openWebExternal('https://explorer.freedata.app')"
>
Explorer map
</button>
</div>
<div class="btn-group">
<button
class="btn btn-sm btn-secondary me-2 bi bi-graph-up"
id="btnStats"
type="button"
data-bs-placement="bottom"
@click="
openWebExternal('https://statistics.freedata.app/')
"
>
Statistics
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-bookmarks me-2"
id="fdWww"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="FreeDATA website"
role="button"
@click="openWebExternal('https://freedata.app')"
>
Website
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-github me-2"
id="ghUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Github"
role="button"
@click="
openWebExternal('https://github.com/dj2ls/freedata')
"
>
Github
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-wikipedia me-2"
id="wikiUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Wiki"
role="button"
@click="openWebExternal('https://wiki.freedata.app')"
>
Wiki
</button>
</div>
<div class="btn-group">
<button
class="btn btn-secondary bi bi-discord"
id="discordUrl"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
title="Discord"
role="button"
@click="openWebExternal('https://discord.freedata.app')"
>
Discord
</button>
</div>
</div>
</div>
<div class="row mt-5">
<h6>Special thanks to</h6>
<hr />
</div>
<div class="row">
<div class="col-4" id="divContrib"></div>
<div class="col-4" id="divContrib2"></div>
<div class="col-4" id="divContrib3"></div>
</div>
</div>
<infoScreen />
</div>
<div
class="tab-pane fade"

View file

@ -71,29 +71,45 @@ function getMaidenheadDistance(dxGrid) {
<th scope="col" id="thTime">
<i id="hslSort" class="bi bi-sort-up"></i>Time
</th>
<th scope="col" id="thFreq">Frequency</th>
<th>&nbsp;</th>
<th scope="col" id="thFreq">Freq</th>
<th scope="col" id="thDxcall">DXCall</th>
<th scope="col" id="thDxgrid">DXGrid</th>
<th scope="col" id="thDist">Distance</th>
<th scope="col" id="thDxgrid">Grid</th>
<th scope="col" id="thDist">Dist</th>
<th scope="col" id="thType">Type</th>
<th scope="col" id="thSnr">SNR (rx/dx)</th>
<th scope="col" id="thSnr">SNR</th>
<!--<th scope="col">Off</th>-->
</tr>
</thead>
<tbody id="heardstations">
<!--https://vuejs.org/guide/essentials/list.html-->
<tr v-for="item in state.heard_stations" :key="item.timestamp">
<td>{{ getDateTime(item.timestamp) }}</td>
<td>{{ item.frequency }}</td>
<td>&nbsp;</td>
<td>
<span class="badge bg-secondary">{{
getDateTime(item.timestamp)
}}</span>
</td>
<td>
<span class="badge bg-secondary"
>{{ item.frequency / 1000 }} kHz</span
>
</td>
<td>
<span class="badge bg-secondary">{{ item.dxcallsign }}</span>
</td>
<td>{{ item.dxgrid }}</td>
<td>{{ getMaidenheadDistance(item.dxgrid) }} km</td>
<td>{{ item.datatype }}</td>
<td>{{ item.snr }}</td>
<td>
<span class="badge bg-secondary">{{ item.dxgrid }}</span>
</td>
<td>
<span class="badge bg-secondary"
>{{ getMaidenheadDistance(item.dxgrid) }} km</span
>
</td>
<td>
<span class="badge bg-secondary">{{ item.datatype }}</span>
</td>
<td>
<span class="badge bg-secondary">{{ item.snr }}</span>
</td>
<!--<td>{{ item.offset }}</td>-->
</tr>
</tbody>

View file

@ -8,7 +8,12 @@ const state = useStateStore(pinia);
import { set_frequency, set_mode, set_rf_level } from "../js/sock.js";
function set_hamlib_frequency() {
function updateFrequencyAndApply(frequency) {
state.new_frequency = frequency;
set_frequency(state.new_frequency);
}
function set_hamlib_frequency_manually() {
set_frequency(state.new_frequency);
}
@ -58,50 +63,156 @@ function set_hamlib_rf_level() {
</div>
</div>
<div class="card-body p-2">
<div class="input-group bottom-0 m-0">
<div class="input-group input-group-sm bottom-0 m-0">
<div class="me-2">
<div class="input-group">
<div class="input-group input-group-sm">
<span class="input-group-text">QRG</span>
<span class="input-group-text">{{ state.frequency }} Hz</span>
<span class="input-group-text">QSY</span>
<input
type="text"
class="form-control"
v-model="state.new_frequency"
style="max-width: 8rem"
pattern="[0-9]*"
list="frequencyDataList"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
/>
<datalist id="frequencyDataList">
<option selected value="7053000">40m | USB | EU, US</option>
<option value="14093000">20m | USB | EU, US</option>
<option value="21093000">15m | USB | EU, US</option>
<option value="24908000">12m | USB | EU, US</option>
<option value="27245000">11m | USB | Ch 25</option>
<option value="27265000">11m | USB | Ch 26</option>
<option value="28093000">10m | USB | EU, US</option>
<option value="50308000">6m | USB | US</option>
<option value="50616000">6m | USB | EU, US</option>
</datalist>
<!-- Dropdown Button -->
<button
class="btn btn-sm btn-outline-success"
type="button"
@click="set_hamlib_frequency"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownMenuButton"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Apply
Select Frequency
</button>
<!-- Dropdown Menu -->
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<li>
<div class="input-group p-1">
<span class="input-group-text">frequency</span>
<input
v-model="state.new_frequency"
style="max-width: 8rem"
pattern="[0-9]*"
type="text"
class="form-control form-control-sm"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
placeholder="Type frequency..."
aria-label="Frequency"
/>
<button
class="btn btn-sm btn-outline-success"
type="button"
@click="set_hamlib_frequency_manually"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
>
<i class="bi bi-check-square"></i>
</button>
</div>
</li>
<!-- Dropdown Divider -->
<li><hr class="dropdown-divider" /></li>
<!-- Dropdown Items -->
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(50616000)"
><strong>50616 kHz</strong>&nbsp;
<span class="badge bg-secondary">6m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(50308000)"
><strong>50308 kHz</strong>&nbsp;
<span class="badge bg-secondary">6m | USB</span>&nbsp;
<span class="badge bg-info">US</span></a
>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(28093000)"
><strong>28093 kHz</strong>&nbsp;
<span class="badge bg-secondary">10m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(27265000)"
><strong>27265 kHz</strong>&nbsp;
<span class="badge bg-secondary">11m | USB</span>&nbsp;
<span class="badge bg-dark">Ch 26</span></a
>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(27245000)"
><strong>27245 kHz</strong>&nbsp;
<span class="badge bg-secondary">11m | USB</span>&nbsp;
<span class="badge bg-dark">Ch 25</span></a
>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(24908000)"
><strong>24908 kHz</strong>&nbsp;
<span class="badge bg-secondary">12m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(21093000)"
><strong>21093 kHz</strong>&nbsp;
<span class="badge bg-secondary">15m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(14093000)"
><strong>14093 kHz</strong>&nbsp;
<span class="badge bg-secondary">20m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
<li>
<a
class="dropdown-item"
href="#"
@click="updateFrequencyAndApply(7053000)"
><strong>7053 kHz</strong>&nbsp;
<span class="badge bg-secondary">40m | USB</span>&nbsp;
<span class="badge bg-info">EU | US</span>
</a>
</li>
</ul>
</div>
</div>
<div class="me-2">
<div class="input-group">
<div class="input-group input-group-sm">
<span class="input-group-text">Mode</span>
<select
class="form-control"
@ -123,7 +234,7 @@ function set_hamlib_rf_level() {
</div>
<div class="me-2">
<div class="input-group">
<div class="input-group input-group-sm">
<span class="input-group-text">Power</span>
<select
class="form-control"

View file

@ -191,15 +191,15 @@ export default {
<div class="card-header p-1">
<div class="container">
<div class="row">
<div class="col-11">
<div class="btn-group" role="group">
<div class="col-11 p-0">
<div class="btn-group h-100" role="group">
<div
class="list-group list-group-horizontal"
class="list-group bg-body-tertiary list-group-horizontal"
id="list-tab"
role="tablist"
>
<a
class="py-1 list-group-item list-group-item-action"
class="py-0 list-group-item list-group-item-dark list-group-item-action"
id="list-waterfall-list"
data-bs-toggle="list"
href="#list-waterfall"
@ -210,7 +210,7 @@ export default {
><strong><i class="bi bi-water"></i></strong
></a>
<a
class="py-1 list-group-item list-group-item-action"
class="py-0 list-group-item list-group-item-dark list-group-item-action"
id="list-scatter-list"
data-bs-toggle="list"
href="#list-scatter"
@ -221,7 +221,7 @@ export default {
><strong><i class="bi bi-border-outer"></i></strong
></a>
<a
class="py-1 list-group-item list-group-item-action"
class="py-0 list-group-item list-group-item-dark list-group-item-action"
id="list-chart-list"
data-bs-toggle="list"
href="#list-chart"

View file

@ -18,14 +18,16 @@ const state = useStateStore(pinia);
<button
class="btn btn-sm btn-secondary me-1"
v-bind:class="{
'bg-danger': state.ptt_state === 'True',
'bg-secondary': state.ptt_state === 'False',
'btn-danger': state.ptt_state === 'True',
'btn-secondary': state.ptt_state === 'False',
}"
id="ptt_state"
type="button"
title="Rig PTT state"
style="pointer-events: auto"
disabled
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-title="PTT trigger state"
>
<i class="bi bi-broadcast-pin" style="font-size: 0.8rem"></i>
</button>
@ -39,10 +41,10 @@ const state = useStateStore(pinia);
data-bs-trigger="hover"
data-bs-html="true"
v-bind:class="{
'bg-danger': state.busy_state === 'BUSY',
'bg-secondary': state.busy_state === 'IDLE',
'btn-danger': state.busy_state === 'BUSY',
'btn-secondary': state.busy_state === 'IDLE',
}"
title="Modem state"
data-bs-title="Modem state"
disabled
style="pointer-events: auto"
>
@ -58,12 +60,12 @@ const state = useStateStore(pinia);
data-bs-trigger="hover"
data-bs-html="true"
v-bind:class="{
'bg-secondary': state.arq_session_state === 'disconnected',
'bg-warning': state.arq_session_state === 'connected',
'btn-secondary': state.arq_session_state === 'disconnected',
'btn-warning': state.arq_session_state === 'connected',
}"
disabled
style="pointer-events: auto"
title="Session state"
data-bs-title="Session state"
>
<i class="bi bi-arrow-left-right" style="font-size: 0.8rem"></i>
</button>
@ -72,10 +74,13 @@ const state = useStateStore(pinia);
class="btn btn-sm btn-secondary me-1"
id="arq_state"
type="button"
title="Data channel state"
data-bs-placement="top"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-title="Data channel state"
v-bind:class="{
'bg-secondary': state.arq_state === 'False',
'bg-warning': state.arq_state === 'True',
'btn-secondary': state.arq_state === 'False',
'btn-warning': state.arq_state === 'True',
}"
disabled
style="pointer-events: auto"
@ -98,7 +103,7 @@ const state = useStateStore(pinia);
-->
<button
class="btn btn-sm disabled me-3"
class="btn btn-sm btn-secondary disabled me-3"
type="button"
data-bs-placement="top"
data-bs-toggle="tooltip"
@ -109,7 +114,7 @@ const state = useStateStore(pinia);
'btn-secondary': state.channel_busy === 'False',
}"
style="pointer-events: auto"
title="Channel busy"
data-bs-title="Channel busy"
>
<i class="bi bi-hourglass"></i>
</button>
@ -119,7 +124,10 @@ const state = useStateStore(pinia);
<button
class="btn btn-sm btn-secondary me-4 disabled"
type="button"
title="What's the frequency, Kenneth?"
data-bs-placement="top"
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-title="What's the frequency, Kenneth?"
style="pointer-events: auto"
>
{{ parseInt(state.frequency) / 1000 }} KHz
@ -142,6 +150,7 @@ const state = useStateStore(pinia);
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-html="true"
data-bs-titel="speed level"
>
<i
class="bi"
@ -172,6 +181,7 @@ const state = useStateStore(pinia);
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-html="true"
data-bs-title="total bytes of transmission"
>
{{ state.arq_total_bytes }}
</button>
@ -184,7 +194,7 @@ const state = useStateStore(pinia);
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-html="true"
title="Current or last connected with station"
data-bs-title="Current or last connected with station"
>
<i class="bi bi-file-earmark-binary" style="font-size: 1rem"></i>
</button>
@ -196,6 +206,7 @@ const state = useStateStore(pinia);
data-bs-toggle="tooltip"
data-bs-trigger="hover"
data-bs-html="true"
data-bs-title="the dxcallsign of the connected station"
>
{{ state.dxcallsign }}
</button>

View file

@ -4,13 +4,14 @@
import { setActivePinia } from "pinia";
import pinia from "../store/index";
setActivePinia(pinia);
import { useStateStore } from "../store/stateStore.js";
const state = useStateStore(pinia);
import { saveSettingsToFile } from "../js/settingsHandler";
import { useChatStore } from "../store/chatStore.js";
const chat = useChatStore(pinia);
import { useSettingsStore } from "../store/settingsStore.js";
const settings = useSettingsStore(pinia);
import {
deleteChatByCallsign,
getNewMessagesByDXCallsign,
@ -23,7 +24,8 @@ function tuneAudio() {
}
function set_audio_level() {
setTxAudioLevel(state.audio_level);
saveSettingsToFile();
setTxAudioLevel(settings.tx_audio_level);
}
function deleteChat() {
@ -1194,7 +1196,7 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
</div>
<div class="input-group input-group-sm mb-1">
<span class="input-group-text">TX Level</span>
<span class="input-group-text">{{ state.audio_level }}</span>
<span class="input-group-text">{{ settings.tx_audio_level }}</span>
<span class="input-group-text w-75">
<input
type="range"
@ -1204,7 +1206,7 @@ const transmissionSpeedChartDataMessageInfo = computed(() => ({
step="1"
id="audioLevelTX"
@click="set_audio_level()"
v-model="state.audio_level"
v-model="settings.tx_audio_level"
/></span>
</div>
</div>

View file

@ -25,7 +25,8 @@ function startStopRigctld() {
break;
case "running":
stopRigctld();
// dirty hack for calling this command twice, otherwise modem won't stop rigctld from time to time
stopRigctld();
break;
default:
}
@ -73,12 +74,12 @@ alert("not yet implemented")
<div class="col-6">
<div
class="list-group list-group-horizontal"
class="list-group bg-body-tertiary list-group-horizontal w-75"
id="rig-control-list-tab"
role="rig-control-tablist"
>
<a
class="py-1 ps-1 pe-1 list-group-item list-group-item-action"
class="p-1 list-group-item list-group-item-dark list-group-item-action"
id="list-rig-control-none-list"
data-bs-toggle="list"
href="#list-rig-control-none"
@ -86,10 +87,10 @@ alert("not yet implemented")
aria-controls="list-rig-control-none"
v-bind:class="{ active: settings.radiocontrol === 'disabled' }"
@click="selectRadioControl()"
>None/Vox</a
>None</a
>
<a
class="py-1 ps-1 pe-1 list-group-item list-group-item-action"
class="p-1 list-group-item list-group-item-dark list-group-item-action"
id="list-rig-control-rigctld-list"
data-bs-toggle="list"
href="#list-rig-control-rigctld"
@ -100,7 +101,7 @@ alert("not yet implemented")
>Rigctld</a
>
<a
class="py-1 ps-1 pe-1 list-group-item list-group-item-action"
class="p-1 list-group-item list-group-item-dark list-group-item-action"
id="list-rig-control-tci-list"
data-bs-toggle="list"
href="#list-rig-control-tci"
@ -138,7 +139,8 @@ alert("not yet implemented")
>
<p class="small">
Modem will not utilize rig control and features will be limited. While
functional; it is recommended to configure hamlib.
functional; it is recommended to configure hamlib. <br>
Use this setting also for <strong> VOX </strong>
</p>
</div>
<div

View file

@ -103,26 +103,5 @@ function startStopModem() {
</button>
</div>
<!--
<div class="btn-toolbar" role="toolbar">
<span data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-trigger="hover" data-bs-html="false"
title="View the received files. This is currently under development!">
<button class="btn btn-sm btn-primary me-2" data-bs-toggle="offcanvas" data-bs-target="#receivedFilesSidebar" id="openReceivedFiles" type="button" > <strong>Files </strong>
<i class="bi bi-file-earmark-arrow-up-fill" style="font-size: 1rem; color: white;"></i>
<i class="bi bi-file-earmark-arrow-down-fill" style="font-size: 1rem; color: white;"></i>
</button>
</span> <span data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-trigger="hover" data-bs-html="false" title="Send files through HF. This is currently under development!">
<button class="btn btn-sm btn-primary me-2" id="openDataModule" data-bs-toggle="offcanvas" data-bs-target="#transmitFileSidebar" type="button" style="display: None;"> <strong>TX File </strong>
<i class="bi bi-file-earmark-arrow-up-fill" style="font-size: 1rem; color: white;"></i>
</button>
</span> <span data-bs-placement="bottom" data-bs-toggle="tooltip" data-bs-trigger="hover" data-bs-html="true"
title="Settings and Info">
</span>
</div>
--></nav>
</nav>
</template>

View file

@ -14,156 +14,164 @@ import settings_exp from "./settings_exp.vue";
aria-labelledby="list-settings-list"
>
<div class="container">
<div class="badge text-bg-warning ms-3">
<i class="bi bi-exclamation-triangle"></i> Please restart the modem
after changing settings!
<div class="badge text-bg-warning m-1">
<h5>
<i class="bi bi-exclamation-triangle"></i> Please restart the modem
after changing settings <i class="bi bi-exclamation-triangle"></i>
</h5>
</div>
<!-- SETTINGS Nav tabs -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button
class="nav-link active"
id="gui-tab"
data-bs-toggle="tab"
data-bs-target="#gui"
type="button"
role="tab"
aria-controls="home"
aria-selected="true"
>
GUI
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="chat-tab"
data-bs-toggle="tab"
data-bs-target="#chat"
type="button"
role="tab"
aria-controls="home"
aria-selected="true"
>
Chat
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="hamlib-tab"
data-bs-toggle="tab"
data-bs-target="#hamlib"
type="button"
role="tab"
aria-controls="profile"
aria-selected="false"
>
Hamlib
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="modem-tab"
data-bs-toggle="tab"
data-bs-target="#modem"
type="button"
role="tab"
aria-controls="profile"
aria-selected="false"
>
Modem
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="web-tab"
data-bs-toggle="tab"
data-bs-target="#web"
type="button"
role="tab"
aria-controls="messages"
aria-selected="false"
>
Web
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="experiments-tab"
data-bs-toggle="tab"
data-bs-target="#experiments"
type="button"
role="tab"
aria-controls="settings"
aria-selected="false"
>
Exp
</button>
</li>
</ul>
<!-- SETTINGS Nav Tab panes -->
<div class="tab-content mt-1">
<!-- GUI tab contents-->
<div
class="tab-pane active"
id="gui"
role="tabpanel"
aria-labelledby="gui-tab"
tabindex="0"
>
<settings_gui />
<div class="card text-center">
<div class="card-header">
<!-- SETTINGS Nav tabs -->
<ul class="nav nav-tabs card-header-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button
class="nav-link active"
id="gui-tab"
data-bs-toggle="tab"
data-bs-target="#gui"
type="button"
role="tab"
aria-controls="home"
aria-selected="true"
>
GUI
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="chat-tab"
data-bs-toggle="tab"
data-bs-target="#chat"
type="button"
role="tab"
aria-controls="home"
aria-selected="true"
>
Chat
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="hamlib-tab"
data-bs-toggle="tab"
data-bs-target="#hamlib"
type="button"
role="tab"
aria-controls="profile"
aria-selected="false"
>
Hamlib
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="modem-tab"
data-bs-toggle="tab"
data-bs-target="#modem"
type="button"
role="tab"
aria-controls="profile"
aria-selected="false"
>
Modem
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="web-tab"
data-bs-toggle="tab"
data-bs-target="#web"
type="button"
role="tab"
aria-controls="messages"
aria-selected="false"
>
Web
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="experiments-tab"
data-bs-toggle="tab"
data-bs-target="#experiments"
type="button"
role="tab"
aria-controls="settings"
aria-selected="false"
>
Exp
</button>
</li>
</ul>
</div>
<div class="card-body">
<!-- SETTINGS Nav Tab panes -->
<div class="tab-content">
<!-- GUI tab contents-->
<div
class="tab-pane active"
id="gui"
role="tabpanel"
aria-labelledby="gui-tab"
tabindex="0"
>
<settings_gui />
</div>
<div
class="tab-pane"
id="chat"
role="tabpanel"
aria-labelledby="chat-tab"
tabindex="0"
>
<settings_chat />
</div>
<div
class="tab-pane"
id="chat"
role="tabpanel"
aria-labelledby="chat-tab"
tabindex="0"
>
<settings_chat />
</div>
<div
class="tab-pane"
id="hamlib"
role="tabpanel"
aria-labelledby="hamlib-tab"
tabindex="0"
>
<settings_hamlib />
</div>
<div
class="tab-pane"
id="modem"
role="tabpanel"
aria-labelledby="modem-tab"
tabindex="0"
>
<settings_modem />
</div>
<div
class="tab-pane"
id="web"
role="tabpanel"
aria-labelledby="web-tab"
tabindex="0"
>
<settings_web />
</div>
<div
class="tab-pane"
id="experiments"
role="tabpanel"
aria-labelledby="experiments-tab"
tabindex="0"
>
<settings_exp />
<div
class="tab-pane"
id="hamlib"
role="tabpanel"
aria-labelledby="hamlib-tab"
tabindex="0"
>
<settings_hamlib />
</div>
<div
class="tab-pane"
id="modem"
role="tabpanel"
aria-labelledby="modem-tab"
tabindex="0"
>
<settings_modem />
</div>
<div
class="tab-pane"
id="web"
role="tabpanel"
aria-labelledby="web-tab"
tabindex="0"
>
<settings_web />
</div>
<div
class="tab-pane"
id="experiments"
role="tabpanel"
aria-labelledby="experiments-tab"
tabindex="0"
>
<settings_exp />
</div>
</div>
</div>
</div>
</div>

View file

@ -112,7 +112,6 @@ function saveSettings() {
id="max_retry_attempts"
@change="saveSettings"
v-model="settings.max_retry_attempts"
disabled
>
<option value="1">1</option>
<option value="2">2</option>

View file

@ -202,9 +202,14 @@ export function newMessage(
filename = chatFileName;
} else {
file = "";
filetype = "text";
filetype = "";
filename = "";
}
console.log(file);
console.log(filetype);
console.log(filename);
var file_checksum = ""; //crc32(file).toString(16).toUpperCase();
var message_type = "transmit";
var command = "msg";
@ -536,6 +541,29 @@ export async function updateAllChat(cleanup) {
} else {
chat.unsorted_chat_list.push(item);
chat.sorted_chat_list = sortChatList();
// check if message is expired
let timeNow = Math.floor(Date.now() / 1000);
let expireTimestamp = timeNow - 10 * 60;
let isExpired = false;
if (expireTimestamp >= item.timestamp) {
isExpired = true;
}
if (item.status == "transmitting" && isExpired) {
console.log("message expired - resetting status");
console.log(item);
databaseUpsert(item.uuid, "status", "failed");
updateUnsortedChatListEntry(item.uuid, "status", "failed");
databaseUpsert(item.uuid, "percent", 0);
updateUnsortedChatListEntry(item.uuid, "percent", 0);
}
// lets update the message if it is failed. Then its always 0 percent
if (item.status == "failed") {
databaseUpsert(item.uuid, "percent", 0);
updateUnsortedChatListEntry(item.uuid, "percent", 0);
databaseUpsert(item.uuid, "bytesperminute", 0);
updateUnsortedChatListEntry(item.uuid, "bytesperminute", 0);
}
}
}
})
@ -861,7 +889,7 @@ export function requestMessageInfo(id) {
// id and uuid are the same
var data = getFromUnsortedChatListByUUID(id);
chat.selectedMessageObject = data;
console.log(data);
if (
typeof data["speed_list"] !== "undefined" &&
data["speed_list"].length > 0
@ -986,14 +1014,28 @@ async function checkForWaitingMessages(dxcall) {
console.log(message);
displayToast("info", "bi bi-info-circle", message, 5000);
console.log(result);
// @ts-expect-error
console.log(result.docs);
// @ts-expect-error
console.log(result.docs.length);
// handle result
// @ts-expect-error
if (result.docs.length > 0) {
// only want to process the first available item object, then return
// this ensures, we are only sending one message at once
// @ts-expect-error
console.log(result.docs[0]);
console.log(
"attempt: " +
// @ts-expect-error
result.docs[0].attempt +
"/" +
settings.max_retry_attempts,
);
// @ts-expect-error
if (result.docs[0].attempt < settings.max_retry_attempts) {
console.log("repeating message...");
// @ts-expect-error
repeatMessageTransmission(result.docs[0].uuid);
}

View file

@ -183,7 +183,7 @@ export function startModem() {
low_bandwidth_mode: settings.low_bandwidth_mode,
tuning_range_fmin: settings.tuning_range_fmin,
tuning_range_fmax: settings.tuning_range_fmax,
//tx_audio_level: settings.tx_audio_level,
tx_audio_level: settings.tx_audio_level,
respond_to_cq: settings.respond_to_cq,
rx_buffer_size: settings.rx_buffer_size,
enable_explorer: settings.enable_explorer,

View file

@ -55,6 +55,7 @@ const configDefaultSettings =
"daemon_port": 3001,\
"rx_audio" : "",\
"tx_audio" : "",\
"tx_audio_level" : 100,\
"mycall": "AA0AA-0",\
"myssid": "0",\
"mygrid": "JN40aa",\

View file

@ -30,7 +30,7 @@ const split_char = "0;1;";
// global to keep track of Modem connection error emissions
var modemShowConnectStateError = 1;
var setTxAudioLevelOnce = true;
// network connection Timeout
setTimeout(connectModem, 2000);
@ -171,6 +171,20 @@ client.on("data", function (socketdata) {
stateStore.arq_state = data["arq_state"];
stateStore.mode = data["mode"];
stateStore.bandwidth = data["bandwidth"];
stateStore.tx_audio_level = data["audio_level"];
// if audio level is different from config one, send new audio level to modem
//console.log(parseInt(stateStore.tx_audio_level))
//console.log(parseInt(settings.tx_audio_level))
if (
parseInt(stateStore.tx_audio_level) !==
parseInt(settings.tx_audio_level) &&
setTxAudioLevelOnce === true
) {
setTxAudioLevelOnce = false;
console.log(setTxAudioLevelOnce);
setTxAudioLevel(settings.tx_audio_level);
}
stateStore.dbfs_level = data["audio_dbfs"];
stateStore.ptt_state = data["ptt_state"];
stateStore.speed_level = data["speed_level"];

View file

@ -1,15 +1,10 @@
import { createApp } from "vue";
import { createPinia } from "pinia";
import { loadSettings } from "./js/settingsHandler";
import "./styles.css";
// Import all of Bootstrap's JS
//import * as bootstrap from 'bootstrap'
import "bootstrap/dist/js/bootstrap.bundle.min.js";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-icons/font/bootstrap-icons.css";
import { Chart, Filler } from "chart.js";
// Register the Filler plugin globally
Chart.register(Filler);
// Import our custom CSS
//import './scss/styles.scss'
@ -19,8 +14,22 @@ const app = createApp(App);
//.mount('#app').$nextTick(() => postMessage({ payload: 'removeLoading' }, '*'))
const pinia = createPinia();
app.mount("#app");
app.use(pinia);
// Import all of Bootstrap's JS
//import * as bootstrap from 'bootstrap'
import * as bootstrap from "bootstrap";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-icons/font/bootstrap-icons.css";
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]',
);
// @ts-expect-error
const tooltipList = [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl),
);
loadSettings();
//import './js/settingsHandler.js'

View file

@ -5,6 +5,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
// audio
var tx_audio = ref();
var rx_audio = ref();
var tx_audio_level = ref();
// network
var modem_host = ref("127.0.0.1");
@ -157,6 +158,7 @@ export const useSettingsStore = defineStore("settingsStore", () => {
enable_mesh_features: enable_mesh_features.value,
tx_audio: tx_audio.value,
rx_audio: rx_audio.value,
tx_audio_level: tx_audio_level.value,
};
return config_export;
@ -224,5 +226,6 @@ export const useSettingsStore = defineStore("settingsStore", () => {
rx_audio,
getSerialDevices,
serial_devices,
tx_audio_level,
};
});

View file

@ -40,7 +40,7 @@ export const useStateStore = defineStore("stateStore", () => {
var audio_recording = ref("");
var hamlib_status = ref("");
var audio_level = ref("");
var tx_audio_level = ref("");
var alc = ref("");
var is_codec2_traffic = ref("");
@ -158,7 +158,7 @@ export const useStateStore = defineStore("stateStore", () => {
arq_total_bytes,
audio_recording,
hamlib_status,
audio_level,
tx_audio_level,
alc,
updateTncState,
arq_transmission_percent,

View file

@ -32,7 +32,6 @@ import structlog
import ujson as json
import config
# signal handler for closing application
def signal_handler(sig, frame):
"""
@ -77,6 +76,39 @@ class DAEMON:
worker = threading.Thread(target=self.worker, name="WORKER", daemon=True)
worker.start()
rigctld_watchdog_thread = threading.Thread(target=self.rigctld_watchdog, name="WORKER", daemon=True)
rigctld_watchdog_thread.start()
def rigctld_watchdog(self):
"""
Check for rigctld status
Returns:
"""
while True:
threading.Event().wait(0.01)
# only continue, if we have a process object initialized
if hasattr(Daemon.rigctldprocess, "returncode"):
if Daemon.rigctldprocess.returncode in [None, "None"] or not Daemon.rigctldstarted:
Daemon.rigctldstarted = True
# outs, errs = Daemon.rigctldprocess.communicate(timeout=10)
# print(f"outs: {outs}")
# print(f"errs: {errs}")
else:
self.log.warning("[DMN] [RIGCTLD] [Watchdog] returncode detected",process=Daemon.rigctldprocess)
Daemon.rigctldstarted = False
# triggering another kill
Daemon.rigctldprocess.kill()
# erase process object
Daemon.rigctldprocess = None
else:
Daemon.rigctldstarted = False
def update_audio_devices(self):
"""
Update audio devices and set to static
@ -192,13 +224,7 @@ class DAEMON:
radiocontrol = data[1]
# check how we want to control the radio
if radiocontrol == "direct":
print("direct hamlib support deprecated - not usable anymore")
sys.exit(1)
elif radiocontrol == "rigctl":
print("rigctl support deprecated - not usable anymore")
sys.exit(1)
elif radiocontrol == "rigctld":
if radiocontrol == "rigctld":
import rigctld as rig
rigctld_ip = data[2]
rigctld_port = data[3]
@ -368,23 +394,25 @@ class DAEMON:
options.append(o)
# append debugging paramter
options.append(("-vvv"))
# disabled as this could be set via gui
#options.append(("-vvv"))
command += options
self.log.info("[DMN] starting rigctld: ", param=command)
if not isWin:
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
# NOTE --> It seems Popen is non blocking, while run is blocking
#proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc = subprocess.Popen(command)
#proc = subprocess.run(command, shell=False, check=True, text=True, capture_output=True)
else:
#On windows, open rigctld in new window for easier troubleshooting
proc = subprocess.Popen(command, creationflags=subprocess.CREATE_NEW_CONSOLE,close_fds=True)
atexit.register(proc.kill)
Daemon.rigctldstarted = True
Daemon.rigctldprocess = proc
atexit.register(proc.kill)
except Exception as err:
self.log.warning("[DMN] err starting rigctld: ", e=err)
@ -467,7 +495,7 @@ class DAEMON:
print(data[24])
if data[24] == "True":
options.append("--mesh")
print(options)
# safe data to config file
config.write_entire_config(data)

View file

@ -81,22 +81,33 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
threading.Event().wait(0.5)
while not SOCKET_QUEUE.empty():
data = SOCKET_QUEUE.get()
sock_data = bytes(data, "utf-8")
sock_data += b"\n" # append line limiter
# send data to all clients
try:
data = SOCKET_QUEUE.get()
sock_data = bytes(data, "utf-8")
sock_data += b"\n" # append line limiter
# send data to all connected clients
for client in CONNECTED_CLIENTS:
try:
client.send(sock_data)
except Exception as err:
self.log.info("[SCK] Connection lost", e=err)
# TODO Check if we really should set connection alive to false.
# This might disconnect all other clients as well...
self.connection_alive = False
try:
self.log.warning("[SCK] removing client from sock", client=client, set=CONNECTED_CLIENTS)
CONNECTED_CLIENTS.remove(client)
except Exception as sockerr:
self.log.warning("[SCK] Err remove client from CONNECTED_CLIENTS", e=sockerr, client=client, set=CONNECTED_CLIENTS)
self.log.info("[SCK] resetting sock")
# TODO Check if we really should set connection alive to false.
# This might disconnect all other clients as well...
self.connection_alive = False
except Exception as err:
self.log.debug("[SCK] catch harmless RuntimeError: Set changed size during iteration", e=err)
self.log.debug("[SCK] err while sending data to sock", e=err)
# we want to transmit scatter data only once to reduce network traffic
ModemParam.scatter = []
@ -237,12 +248,15 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
ThreadedTCPRequestHandler.modem_set_record_audio(None, received_json)
else:
self.modem_set_record_audio(received_json)
# SET TX AUDIO LEVEL
if received_json["type"] == "set" and received_json["command"] == "tx_audio_level":
if TESTMODE:
ThreadedTCPRequestHandler.modem_set_tx_audio_level(None, received_json)
else:
self.modem_set_tx_audio_level(received_json)
# TRANSMIT TEST FRAME
if received_json["type"] == "set" and received_json["command"] == "send_test_frame":
if TESTMODE:
@ -991,7 +1005,7 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
if (
received_json["type"] == "set"
and received_json["command"] == "start_modem"
and not Daemon.modemstarted
# and not Daemon.modemstarted
):
self.daemon_start_modem(received_json)
@ -1130,6 +1144,9 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
try:
log.warning("[SCK] Stopping Modem")
Daemon.modemstarted = False
# we need to run this twice, otherwise process won't be stopped
Daemon.modemprocess.kill()
threading.Event().wait(0.3)
Daemon.modemprocess.kill()
# unregister process from atexit to avoid process zombies
atexit.unregister(Daemon.modemprocess.kill)
@ -1409,10 +1426,11 @@ def send_modem_state():
try:
json_out = json.dumps(output)
except Exception as e:
log.warning("[SCK] error while json conversion for modem state", e=e)
return json_out
except Exception as e:
log.warning("[SCK] error while json conversion for modem state", e=e, data=output)
return json_out
def command_response(command, status):