Merge pull request #657 from DJ2LS/dev-radio-control

adjusted radio control and some more fixes
This commit is contained in:
DJ2LS 2024-02-21 17:19:50 +01:00 committed by GitHub
commit f33222794b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 202 additions and 92 deletions

View file

@ -123,7 +123,7 @@ const beaconHistogramData = computed(() => ({
<div class="col-9 border-start vh-100 p-0">
<div class="d-flex flex-column vh-100">
<!-- Top Navbar -->
<nav class="navbar sticky-top bg-body-tertiary shadow">
<nav class="navbar sticky-top z-0 bg-body-tertiary shadow">
<div class="input-group mb-0 p-0 w-25">
<button type="button" class="btn btn-outline-secondary" disabled>
Beacons

View file

@ -8,7 +8,7 @@ import "../../node_modules/gridstack/dist/gridstack.min.css";
import { GridStack } from "gridstack";
import { useStateStore } from "../store/stateStore.js";
const state = useStateStore(pinia);
import { setRadioParameters } from "../js/api";
import { setRadioParametersFrequency, setRadioParametersMode, setRadioParametersRFLevel } from "../js/api";
import { saveLocalSettingsToConfig, settingsStore } from "../store/settingsStore";
import active_heard_stations from "./grid/grid_active_heard_stations.vue";
@ -251,14 +251,22 @@ new gridWidget(
//New new widget ID should be 20
];
function updateFrequencyAndApply(frequency) {
state.new_frequency = frequency;
set_radio_parameters();
set_radio_parameter_frequency();
}
function set_radio_parameters(){
setRadioParameters(state.new_frequency, state.mode, state.rf_level);
function set_radio_parameter_frequency(){
setRadioParametersFrequency(state.new_frequency)
}
function set_radio_parameter_mode(){
setRadioParametersMode(state.mode)
}
function set_radio_parameter_rflevel(){
setRadioParametersRFLevel(state.rf_level)
}
@ -358,19 +366,21 @@ onMounted(() => {
setGridEditState();
});
function onChange(event, changeItems) {
// update item position
changeItems.forEach((item) => {
var widget = items.value.find((w) => w.id == item.id);
if (!widget) {
console.error("Widget not found: " + item.id);
return;
if (typeof changeItems !== "undefined"){
// update item position
changeItems.forEach((item) => {
var widget = items.value.find((w) => w.id == item.id);
if (!widget) {
console.error("Widget not found: " + item.id);
return;
}
widget.x = item.x;
widget.y = item.y;
widget.w = item.w;
widget.h = item.h;
});
saveGridLayout();
}
widget.x = item.x;
widget.y = item.y;
widget.w = item.w;
widget.h = item.h;
});
saveGridLayout();
}
function restoreGridLayoutFromConfig(){
//Try to load grid from saved config

View file

@ -1,15 +1,26 @@
<script setup lang="ts">
import { setActivePinia } from "pinia";
import pinia from "../../store/index";
import { setRadioParameters } from "../../js/api";
import { setRadioParametersFrequency, setRadioParametersMode, setRadioParametersRFLevel } from "../../js/api";
setActivePinia(pinia);
import { useStateStore } from "../../store/stateStore.js";
const state = useStateStore(pinia);
function set_radio_parameters() {
setRadioParameters(state.frequency, state.mode, state.rf_level);
function set_radio_parameter_frequency(){
setRadioParametersFrequency(state.new_frequency)
}
function set_radio_parameter_mode(){
setRadioParametersMode(state.mode)
}
function set_radio_parameter_rflevel(){
setRadioParametersRFLevel(state.rf_level)
}
</script>
<template>
@ -47,18 +58,15 @@ function set_radio_parameters() {
<select
class="form-control"
v-model="state.mode"
@click="set_radio_parameters()"
@click="set_radio_parameter_mode()"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
>
<option selected value="">---</option>
<option value="USB">USB</option>
<option value="LSB">LSB</option>
<option value="USB-D">USB-D</option>
<option value="PKTUSB">PKT-U</option>
<option value="PKTLSB">PKT-L</option>
<option value="AM">AM</option>
<option value="FM">FM</option>
<option value="PKTFM">PKTFM</option>
</select>
</div>
</div>
@ -69,7 +77,7 @@ function set_radio_parameters() {
<select
class="form-control"
v-model="state.rf_level"
@click="set_radio_parameters()"
@click="set_radio_parameter_rflevel()"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"

View file

@ -28,8 +28,7 @@ import { getFreedataMessages } from "../js/api";
<div
aria-live="polite"
aria-atomic="true"
class="position-relative"
style="z-index: 500"
class="position-relative z-3"
>
<div
class="toast-container position-absolute top-0 end-0 p-3"

View file

@ -6,16 +6,25 @@ setActivePinia(pinia);
import { useStateStore } from "../store/stateStore.js";
const state = useStateStore(pinia);
import { setRadioParameters } from "../js/api";
import { setRadioParametersFrequency, setRadioParametersMode, setRadioParametersRFLevel } from "../js/api";
function updateFrequencyAndApply(frequency) {
state.new_frequency = frequency;
set_radio_parameters();
set_radio_parameter_frequency();
}
function set_radio_parameters() {
setRadioParameters(state.new_frequency, state.mode, state.rf_level);
function set_radio_parameter_frequency(){
setRadioParametersFrequency(state.new_frequency)
}
function set_radio_parameter_mode(){
setRadioParametersMode(state.mode)
}
function set_radio_parameter_rflevel(){
setRadioParametersRFLevel(state.rf_level)
}
</script>
<template>
@ -207,18 +216,14 @@ function set_radio_parameters() {
<select
class="form-control"
v-model="state.mode"
@click="set_radio_parameters()"
@click="set_radio_parameter_mode()"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"
>
<option value="USB">USB</option>
<option value="LSB">LSB</option>
<option value="PKTUSB">PKT-U</option>
<option value="PKTLSB">PKT-L</option>
<option value="AM">AM</option>
<option value="FM">FM</option>
<option value="PKTFM">PKTFM</option>
<option value="USB">USB</option>
<option value="USB-D">USB-D</option>
<option value="PKTUSB">PKT-U</option>
</select>
</div>
</div>
@ -229,7 +234,7 @@ function set_radio_parameters() {
<select
class="form-control"
v-model="state.rf_level"
@click="set_radio_parameters()"
@click="set_radio_parameter_rflevel()"
v-bind:class="{
disabled: state.hamlib_status === 'disconnected',
}"

View file

@ -2,7 +2,7 @@
import { Modal } from "bootstrap";
import { onMounted } from "vue";
import infoScreen_updater from "./infoScreen_updater.vue";
import settings_updater_core from "./settings_updater_core.vue";
import { setActivePinia } from "pinia";
import pinia from "../store/index";
@ -427,7 +427,7 @@ function testHamlib() {
Modem version | {{ state.modem_version }}
</button>
<div :class="updateAvailable === '1' ? '' : 'd-none'">
<infoScreen_updater />
<settings_updater_core />
</div>
</div>
</div>

View file

@ -10,7 +10,7 @@ import { settingsStore as settings } from "../store/settingsStore.js";
</script>
<template>
<nav class="navbar bg-body-tertiary border-bottom">
<nav class="navbar bg-body-tertiary border-bottom z-0">
<div class="mx-auto">
<span class="badge bg-secondary me-4">
Modem Connection {{ state.modem_connection }}

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import settings_updater from "./settings_updater.vue";
import settings_station from "./settings_station.vue";
import settings_gui from "./settings_gui.vue";
import settings_chat from "./settings_chat.vue";
@ -22,6 +23,20 @@ import settings_exp from "./settings_exp.vue";
<li class="nav-item" role="presentation">
<button
class="nav-link active"
id="updater-tab"
data-bs-toggle="tab"
data-bs-target="#updater"
type="button"
role="tab"
aria-controls="home"
aria-selected="true"
>
Updater
</button>
</li>
<li class="nav-item" role="presentation">
<button
class="nav-link"
id="station-tab"
data-bs-toggle="tab"
data-bs-target="#station"
@ -126,10 +141,23 @@ import settings_exp from "./settings_exp.vue";
>
<!-- SETTINGS Nav Tab panes -->
<!-- Station tab contents-->
<!-- Updater tab contents-->
<div class="tab-content">
<div
class="tab-pane active"
id="updater"
role="tabpanel"
aria-labelledby="updater-tab"
tabindex="0"
>
<settings_updater />
</div>
</div>
<!-- Station tab contents-->
<div class="tab-content">
<div
class="tab-pane"
id="station"
role="tabpanel"
aria-labelledby="station-tab"

View file

@ -0,0 +1,16 @@
<script setup lang="ts">
import settings_updater_core from "./settings_updater_core.vue";
</script>
<template>
<div>
<div class="alert alert-warning" role="alert">
The updater might not working, yet! Please update manually if you are running into problems!
</div>
<div class="alert alert-warning" role="alert">
The updater doesnt contain the server related parts, yet! We are discussing this topic actually, feel free contributing with your opinion on Discord!
</div>
<settings_updater_core />
</div>
</template>

View file

@ -142,11 +142,19 @@ export async function getModemState() {
return await apiGet("/modem/state");
}
export async function setRadioParameters(frequency, mode, rf_level) {
export async function setRadioParametersFrequency(frequency) {
return await apiPost("/radio", {
radio_frequency: frequency,
radio_mode: mode,
radio_rf_level: rf_level,
});
}
export async function setRadioParametersMode(mode) {
return await apiPost("/radio", {
radio_mode: mode
});
}
export async function setRadioParametersRFLevel(rf_level) {
return await apiPost("/radio", {
radio_rf_level: rf_level
});
}
export async function getRadioStatus() {

View file

@ -16,6 +16,7 @@ import {
getModemState,
} from "./api";
import { processFreedataMessages } from "./messagesHandler.ts";
import { processRadioStatus } from "./radioHandler.ts";
// ----------------- init pinia stores -------------
import { setActivePinia } from "pinia";
@ -99,6 +100,7 @@ export function eventDispatcher(data) {
getAudioDevices();
getSerialDevices();
getFreedataMessages();
processRadioStatus();
return;
case "stopped":
@ -112,6 +114,7 @@ export function eventDispatcher(data) {
getAudioDevices();
getSerialDevices();
getFreedataMessages();
processRadioStatus();
return;
case "failed":
@ -144,6 +147,7 @@ export function eventDispatcher(data) {
getSerialDevices();
getFreedataMessages();
processFreedataMessages();
processRadioStatus();
return;

View file

@ -64,7 +64,7 @@ export function sortByPropertyDesc(property) {
* @returns true or false if callsign appears to be valid with an SSID
*/
export function validateCallsignWithSSID(callsign: string) {
var patt = new RegExp("^[A-Z]+[0-9][A-Z]*-(1[0-5]|[0-9])$");
var patt = new RegExp("^[A-Za-z0-9]{1,7}-[0-9]{1,3}$");
callsign = callsign;
if (
callsign === undefined ||
@ -85,7 +85,7 @@ export function validateCallsignWithSSID(callsign: string) {
* @returns true or false if callsign appears to be valid without an SSID
*/
export function validateCallsignWithoutSSID(callsign: string) {
var patt = new RegExp("^[A-Z]+[0-9][A-Z]+$");
var patt = new RegExp("^[A-Za-z0-9]{1,7}$");
if (
callsign === undefined ||

View file

@ -82,7 +82,6 @@ function createSortedMessagesList(data: {
}
export function newMessage(dxcall, body, attachments) {
console.log(attachments);
sendFreedataMessage(dxcall, body, attachments);
}

View file

@ -0,0 +1,20 @@
// pinia store setup
import { setActivePinia } from "pinia";
import pinia from "../store/index";
setActivePinia(pinia);
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
import { useStateStore } from "../store/stateStore";
const stateStore = useStateStore(pinia);
import {
getRadioStatus,
} from "./api";
export async function processRadioStatus(){
let result = await getRadioStatus()
stateStore.mode = result.radio_mode
stateStore.frequency = result.radio_frequency
stateStore.rf_level = result.radio_rf_level
}

View file

@ -22,14 +22,18 @@ class ARQRawCommand(TxCommand):
self.data = base64.b64decode(apiParams['data'])
def run(self, event_queue: Queue, modem):
self.emit_event(event_queue)
self.logger.info(self.log_message())
try:
self.emit_event(event_queue)
self.logger.info(self.log_message())
prepared_data, type_byte = self.arq_data_type_handler.prepare(self.data, self.type)
prepared_data, type_byte = self.arq_data_type_handler.prepare(self.data, self.type)
iss = ARQSessionISS(self.config, modem, self.dxcall, self.state_manager, prepared_data, type_byte)
if iss.id:
self.state_manager.register_arq_iss_session(iss)
iss.start()
return iss
except Exception as e:
self.log(f"Error starting ARQ session: {e}", isWarning=True)
iss = ARQSessionISS(self.config, modem, self.dxcall, self.state_manager, prepared_data, type_byte)
if iss.id:
self.state_manager.register_arq_iss_session(iss)
iss.start()
return iss
return False

View file

@ -27,24 +27,26 @@ class SendMessageCommand(TxCommand):
if not first_queued_message:
self.log("No queued message in database.")
return
try:
self.log(f"Queued message found: {first_queued_message['id']}")
DatabaseManagerMessages(self.event_manager).update_message(first_queued_message["id"], update_data={'status': 'transmitting'})
message_dict = DatabaseManagerMessages(self.event_manager).get_message_by_id(first_queued_message["id"])
message = MessageP2P.from_api_params(message_dict['origin'], message_dict)
self.log(f"Queued message found: {first_queued_message['id']}")
DatabaseManagerMessages(self.event_manager).update_message(first_queued_message["id"], update_data={'status': 'transmitting'})
message_dict = DatabaseManagerMessages(self.event_manager).get_message_by_id(first_queued_message["id"])
message = MessageP2P.from_api_params(message_dict['origin'], message_dict)
# Convert JSON string to bytes (using UTF-8 encoding)
payload = message.to_payload().encode('utf-8')
json_bytearray = bytearray(payload)
data, data_type = self.arq_data_type_handler.prepare(json_bytearray, ARQ_SESSION_TYPES.p2pmsg_lzma)
# Convert JSON string to bytes (using UTF-8 encoding)
payload = message.to_payload().encode('utf-8')
json_bytearray = bytearray(payload)
data, data_type = self.arq_data_type_handler.prepare(json_bytearray, ARQ_SESSION_TYPES.p2pmsg_lzma)
iss = ARQSessionISS(self.config,
modem,
self.message.destination,
self.state_manager,
data,
data_type
)
iss = ARQSessionISS(self.config,
modem,
self.message.destination,
self.state_manager,
data,
data_type
)
self.state_manager.register_arq_iss_session(iss)
iss.start()
self.state_manager.register_arq_iss_session(iss)
iss.start()
except Exception as e:
self.log(f"Error starting ARQ session: {e}", isWarning=True)

View file

@ -203,7 +203,7 @@ class radio:
self.parameters['alc'] = self.send_command('l ALC')
self.parameters['strength'] = self.send_command('l STRENGTH')
self.parameters['rf'] = self.send_command('l RFPOWER') # RF, RFPOWER
self.parameters['rf'] = int(float(self.send_command('l RFPOWER')) * 100) # RF, RFPOWER
"""Return the latest fetched parameters."""
return self.parameters

View file

@ -72,11 +72,14 @@ def validate(req, param, validator, isRequired = True):
# Takes a transmit command and puts it in the transmit command queue
def enqueue_tx_command(cmd_class, params = {}):
command = cmd_class(app.config_manager.read(), app.state_manager, app.event_manager, params)
app.logger.info(f"Command {command.get_name()} running...")
if command.run(app.modem_events, app.service_manager.modem): # TODO remove the app.modem_event custom queue
return True
return False
try:
command = cmd_class(app.config_manager.read(), app.state_manager, app.event_manager, params)
app.logger.info(f"Command {command.get_name()} running...")
if command.run(app.modem_events, app.service_manager.modem): # TODO remove the app.modem_event custom queue
return True
except Exception as e:
app.logger.warning(f"Command {command.get_name()} failed...: {e}")
return False
## REST API
@app.route('/', methods=['GET'])
@ -233,9 +236,12 @@ def post_modem_send_raw_stop():
@app.route('/radio', methods=['GET', 'POST'])
def get_post_radio():
if request.method in ['POST']:
app.radio_manager.set_frequency(request.json['radio_frequency'])
app.radio_manager.set_mode(request.json['radio_mode'])
app.radio_manager.set_rf_level(int(request.json['radio_rf_level']))
if "radio_frequency" in request.json:
app.radio_manager.set_frequency(request.json['radio_frequency'])
if "radio_mode" in request.json:
app.radio_manager.set_mode(request.json['radio_mode'])
if "radio_rf_level" in request.json:
app.radio_manager.set_rf_level(int(request.json['radio_rf_level']))
return api_response(request.json)
elif request.method == 'GET':
@ -245,11 +251,12 @@ def get_post_radio():
def get_post_freedata_message():
if request.method in ['GET']:
result = DatabaseManagerMessages(app.event_manager).get_all_messages_json()
return api_response(result, 200)
if enqueue_tx_command(command_message_send.SendMessageCommand, request.json):
return api_response(request.json, 200)
else:
api_abort('Error executing command...', 500)
return api_response(result)
if request.method in ['POST']:
enqueue_tx_command(command_message_send.SendMessageCommand, request.json)
return api_response(request.json)
api_abort('Error executing command...', 500)
@app.route('/freedata/messages/<string:message_id>', methods=['GET', 'POST', 'PATCH', 'DELETE'])
def handle_freedata_message(message_id):