[refactor](trx-rs): resolve all improvement areas (P0-P3)
Addresses every item in docs/Improvement-Areas.md:
P0 - Plugin signing: new src/trx-app/src/plugins.rs with SHA-256 checksum
manifest, filename allowlisting, API version compatibility checks,
and cross-platform file permission validation.
P1 - Session store mutex poisoning: all .unwrap() calls on RwLock/Mutex in
auth.rs replaced with .unwrap_or_else(|e| e.into_inner()) + warning logs.
- TCP listener rate limiting: added ConnectionTracker with per-IP connection
cap (10 concurrent connections per IP).
- RigState refactoring: decoder fields grouped into DecoderConfig and
DecoderResetSeqs sub-structs with #[serde(flatten)] for wire compat.
- spawn_blocking timeout: satellite pass computation wrapped in 30s timeout.
P2 - Command handler macro: rig_command! macro generates 7 unit-struct command
implementations, reducing ~200 lines of boilerplate.
- Protocol versioning: added protocol_version field to ClientEnvelope and
ClientResponse; improved unknown command error handling in parse_envelope.
- Unsafe string: replaced from_utf8_unchecked with safe from_utf8().expect().
- Dead code: removed 2 unnecessary annotations, documented remaining 4.
P3 - Tests: added 4 unit tests for history_store.rs (round-trip, expiry, etc).
- FT-817 VFO: improved inference for ambiguous same-frequency case.
- Configurator: implemented serial port detection via tokio_serial.
- Plugin versioning: integrated into plugin manifest (api_version field).
- Naming: documented as intentional semantic distinctions, not inconsistencies.
https://claude.ai/code/session_01Gj1vEkP6GKVcVaMqzFW885
Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -15,5 +15,8 @@ pub type DynResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
pub use rig::command::RigCommand;
|
||||
pub use rig::request::RigRequest;
|
||||
pub use rig::response::{RigError, RigResult};
|
||||
pub use rig::state::{RdsData, RigFilterState, RigMode, RigSnapshot, RigState, WfmDenoiseLevel};
|
||||
pub use rig::state::{
|
||||
DecoderConfig, DecoderResetSeqs, RdsData, RigFilterState, RigMode, RigSnapshot, RigState,
|
||||
WfmDenoiseLevel,
|
||||
};
|
||||
pub use rig::AudioSource;
|
||||
|
||||
@@ -142,7 +142,73 @@ pub enum CommandResult {
|
||||
// Concrete Command Implementations
|
||||
// ============================================================================
|
||||
|
||||
/// Command to set the rig frequency.
|
||||
/// Macro to generate unit-struct command implementations with standard
|
||||
/// precondition checks, reducing repetitive boilerplate.
|
||||
///
|
||||
/// # Syntax
|
||||
///
|
||||
/// ```ignore
|
||||
/// rig_command! {
|
||||
/// /// Doc comment
|
||||
/// UnitCommand("Name") {
|
||||
/// preconditions: [initialized, unlocked],
|
||||
/// execute: |executor| { executor.method().await?; Ok(CommandResult::Variant) },
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! rig_command {
|
||||
// Unit struct variant (no fields).
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$name:ident ($cmd_name:expr) {
|
||||
preconditions: [$($precond:ident),*],
|
||||
execute: |$exec:ident| $body:expr,
|
||||
}
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct $name;
|
||||
|
||||
impl RigCommandHandler for $name {
|
||||
fn name(&self) -> &'static str {
|
||||
$cmd_name
|
||||
}
|
||||
|
||||
fn can_execute(&self, _ctx: &dyn CommandContext) -> ValidationResult {
|
||||
$(rig_command!(@check _ctx, $precond);)*
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
$exec: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move { $body })
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Precondition expansion helpers.
|
||||
(@check $ctx:ident, initialized) => {
|
||||
if !$ctx.is_initialized() {
|
||||
return ValidationResult::InvalidState("Rig not initialized".into());
|
||||
}
|
||||
};
|
||||
(@check $ctx:ident, unlocked) => {
|
||||
if $ctx.is_locked() {
|
||||
return ValidationResult::Locked;
|
||||
}
|
||||
};
|
||||
(@check $ctx:ident, not_transmitting) => {
|
||||
if $ctx.is_transmitting() {
|
||||
return ValidationResult::InvalidState(
|
||||
"Cannot power off while transmitting".into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Command to set the rig frequency (custom validation for freq != 0).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SetFreqCommand {
|
||||
pub freq: Freq,
|
||||
@@ -258,167 +324,6 @@ impl RigCommandHandler for SetPttCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to power on the rig.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PowerOnCommand;
|
||||
|
||||
impl RigCommandHandler for PowerOnCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"PowerOn"
|
||||
}
|
||||
|
||||
fn can_execute(&self, _ctx: &dyn CommandContext) -> ValidationResult {
|
||||
// Power on can always be attempted
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.power_on().await?;
|
||||
Ok(CommandResult::PowerUpdated(true))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to power off the rig.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PowerOffCommand;
|
||||
|
||||
impl RigCommandHandler for PowerOffCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"PowerOff"
|
||||
}
|
||||
|
||||
fn can_execute(&self, ctx: &dyn CommandContext) -> ValidationResult {
|
||||
if ctx.is_transmitting() {
|
||||
return ValidationResult::InvalidState("Cannot power off while transmitting".into());
|
||||
}
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.power_off().await?;
|
||||
Ok(CommandResult::PowerUpdated(false))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to toggle VFO.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ToggleVfoCommand;
|
||||
|
||||
impl RigCommandHandler for ToggleVfoCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"ToggleVfo"
|
||||
}
|
||||
|
||||
fn can_execute(&self, ctx: &dyn CommandContext) -> ValidationResult {
|
||||
if !ctx.is_initialized() {
|
||||
return ValidationResult::InvalidState("Rig not initialized".into());
|
||||
}
|
||||
if ctx.is_locked() {
|
||||
return ValidationResult::Locked;
|
||||
}
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.toggle_vfo().await?;
|
||||
Ok(CommandResult::RefreshRequired)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to lock the panel.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LockCommand;
|
||||
|
||||
impl RigCommandHandler for LockCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"Lock"
|
||||
}
|
||||
|
||||
fn can_execute(&self, ctx: &dyn CommandContext) -> ValidationResult {
|
||||
if !ctx.is_initialized() {
|
||||
return ValidationResult::InvalidState("Rig not initialized".into());
|
||||
}
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.lock().await?;
|
||||
Ok(CommandResult::LockUpdated(true))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to unlock the panel.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnlockCommand;
|
||||
|
||||
impl RigCommandHandler for UnlockCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"Unlock"
|
||||
}
|
||||
|
||||
fn can_execute(&self, _ctx: &dyn CommandContext) -> ValidationResult {
|
||||
// Unlock can always be attempted
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.unlock().await?;
|
||||
Ok(CommandResult::LockUpdated(false))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to get TX limit.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GetTxLimitCommand;
|
||||
|
||||
impl RigCommandHandler for GetTxLimitCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"GetTxLimit"
|
||||
}
|
||||
|
||||
fn can_execute(&self, ctx: &dyn CommandContext) -> ValidationResult {
|
||||
if !ctx.is_initialized() {
|
||||
return ValidationResult::InvalidState("Rig not initialized".into());
|
||||
}
|
||||
ValidationResult::Ok
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
let limit = executor.get_tx_limit().await?;
|
||||
Ok(CommandResult::TxLimitUpdated(limit))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to set TX limit.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SetTxLimitCommand {
|
||||
@@ -455,28 +360,61 @@ impl RigCommandHandler for SetTxLimitCommand {
|
||||
}
|
||||
}
|
||||
|
||||
/// Command to get current state snapshot.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GetSnapshotCommand;
|
||||
// --- Macro-generated unit commands ---
|
||||
|
||||
impl RigCommandHandler for GetSnapshotCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"GetSnapshot"
|
||||
rig_command! {
|
||||
/// Command to power on the rig.
|
||||
PowerOnCommand("PowerOn") {
|
||||
preconditions: [],
|
||||
execute: |executor| { executor.power_on().await?; Ok(CommandResult::PowerUpdated(true)) },
|
||||
}
|
||||
}
|
||||
|
||||
fn can_execute(&self, _ctx: &dyn CommandContext) -> ValidationResult {
|
||||
// Getting snapshot can always be attempted
|
||||
ValidationResult::Ok
|
||||
rig_command! {
|
||||
/// Command to power off the rig.
|
||||
PowerOffCommand("PowerOff") {
|
||||
preconditions: [not_transmitting],
|
||||
execute: |executor| { executor.power_off().await?; Ok(CommandResult::PowerUpdated(false)) },
|
||||
}
|
||||
}
|
||||
|
||||
fn execute<'a>(
|
||||
&'a self,
|
||||
executor: &'a mut dyn CommandExecutor,
|
||||
) -> Pin<Box<dyn Future<Output = DynResult<CommandResult>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
executor.refresh_state().await?;
|
||||
Ok(CommandResult::RefreshRequired)
|
||||
})
|
||||
rig_command! {
|
||||
/// Command to toggle VFO.
|
||||
ToggleVfoCommand("ToggleVfo") {
|
||||
preconditions: [initialized, unlocked],
|
||||
execute: |executor| { executor.toggle_vfo().await?; Ok(CommandResult::RefreshRequired) },
|
||||
}
|
||||
}
|
||||
|
||||
rig_command! {
|
||||
/// Command to lock the panel.
|
||||
LockCommand("Lock") {
|
||||
preconditions: [initialized],
|
||||
execute: |executor| { executor.lock().await?; Ok(CommandResult::LockUpdated(true)) },
|
||||
}
|
||||
}
|
||||
|
||||
rig_command! {
|
||||
/// Command to unlock the panel.
|
||||
UnlockCommand("Unlock") {
|
||||
preconditions: [],
|
||||
execute: |executor| { executor.unlock().await?; Ok(CommandResult::LockUpdated(false)) },
|
||||
}
|
||||
}
|
||||
|
||||
rig_command! {
|
||||
/// Command to get TX limit.
|
||||
GetTxLimitCommand("GetTxLimit") {
|
||||
preconditions: [initialized],
|
||||
execute: |executor| { let limit = executor.get_tx_limit().await?; Ok(CommandResult::TxLimitUpdated(limit)) },
|
||||
}
|
||||
}
|
||||
|
||||
rig_command! {
|
||||
/// Command to get current state snapshot.
|
||||
GetSnapshotCommand("GetSnapshot") {
|
||||
preconditions: [],
|
||||
execute: |executor| { executor.refresh_state().await?; Ok(CommandResult::RefreshRequired) },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,55 @@ use uuid::Uuid;
|
||||
use crate::radio::freq::Freq;
|
||||
use crate::rig::{RigControl, RigInfo, RigRxStatus, RigStatus, RigStatusProvider, RigTxStatus};
|
||||
|
||||
/// Decoder enable/disable flags grouped for cleaner state management.
|
||||
///
|
||||
/// Flattened into `RigState` and `RigSnapshot` so the JSON wire format is
|
||||
/// unchanged (backward compatible with existing clients).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct DecoderConfig {
|
||||
#[serde(default)]
|
||||
pub aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub hf_aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub cw_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft8_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft4_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft2_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub wspr_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub lrpt_decode_enabled: bool,
|
||||
}
|
||||
|
||||
/// Decoder reset sequence counters for invalidating decoder windows.
|
||||
///
|
||||
/// Each counter is incremented when the corresponding decoder is reset
|
||||
/// (e.g. frequency change, explicit reset command). Decoder tasks compare
|
||||
/// against a cached value to detect resets without being fully disabled.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
pub struct DecoderResetSeqs {
|
||||
#[serde(default, skip_serializing)]
|
||||
pub aprs_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub hf_aprs_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub cw_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft8_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft4_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft2_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub wspr_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub lrpt_decode_reset_seq: u64,
|
||||
}
|
||||
|
||||
/// Simple transceiver state representation held by the rig task.
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
pub struct RigState {
|
||||
@@ -31,22 +80,9 @@ pub struct RigState {
|
||||
pub pskreporter_status: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub aprs_is_status: Option<String>,
|
||||
#[serde(default)]
|
||||
pub aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub hf_aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub cw_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft8_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft4_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft2_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub wspr_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub lrpt_decode_enabled: bool,
|
||||
/// Decoder enable/disable flags.
|
||||
#[serde(flatten)]
|
||||
pub decoders: DecoderConfig,
|
||||
#[serde(default)]
|
||||
pub cw_auto: bool,
|
||||
#[serde(default)]
|
||||
@@ -65,22 +101,9 @@ pub struct RigState {
|
||||
/// Skipped in serde (not part of persistent state); flows into RigSnapshot on demand.
|
||||
#[serde(skip)]
|
||||
pub vchan_rds: Option<Vec<VchanRdsEntry>>,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub aprs_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub hf_aprs_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub cw_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft8_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft4_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub ft2_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub wspr_decode_reset_seq: u64,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub lrpt_decode_reset_seq: u64,
|
||||
/// Decoder reset sequence counters.
|
||||
#[serde(flatten)]
|
||||
pub reset_seqs: DecoderResetSeqs,
|
||||
}
|
||||
|
||||
/// Mode supported by the rig.
|
||||
@@ -156,30 +179,14 @@ impl RigState {
|
||||
server_longitude: None,
|
||||
pskreporter_status: None,
|
||||
aprs_is_status: None,
|
||||
aprs_decode_enabled: false,
|
||||
hf_aprs_decode_enabled: false,
|
||||
cw_decode_enabled: false,
|
||||
ft8_decode_enabled: false,
|
||||
ft4_decode_enabled: false,
|
||||
ft2_decode_enabled: false,
|
||||
wspr_decode_enabled: false,
|
||||
|
||||
lrpt_decode_enabled: false,
|
||||
decoders: DecoderConfig::default(),
|
||||
cw_auto: true,
|
||||
cw_wpm: 15,
|
||||
cw_tone_hz: 700,
|
||||
filter: None,
|
||||
spectrum: None,
|
||||
vchan_rds: None,
|
||||
aprs_decode_reset_seq: 0,
|
||||
hf_aprs_decode_reset_seq: 0,
|
||||
cw_decode_reset_seq: 0,
|
||||
ft8_decode_reset_seq: 0,
|
||||
ft4_decode_reset_seq: 0,
|
||||
ft2_decode_reset_seq: 0,
|
||||
wspr_decode_reset_seq: 0,
|
||||
|
||||
lrpt_decode_reset_seq: 0,
|
||||
reset_seqs: DecoderResetSeqs::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,29 +236,14 @@ impl RigState {
|
||||
server_longitude: snapshot.server_longitude,
|
||||
pskreporter_status: snapshot.pskreporter_status,
|
||||
aprs_is_status: snapshot.aprs_is_status,
|
||||
aprs_decode_enabled: snapshot.aprs_decode_enabled,
|
||||
hf_aprs_decode_enabled: snapshot.hf_aprs_decode_enabled,
|
||||
cw_decode_enabled: snapshot.cw_decode_enabled,
|
||||
decoders: snapshot.decoders,
|
||||
cw_auto: snapshot.cw_auto,
|
||||
cw_wpm: snapshot.cw_wpm,
|
||||
cw_tone_hz: snapshot.cw_tone_hz,
|
||||
ft8_decode_enabled: snapshot.ft8_decode_enabled,
|
||||
ft4_decode_enabled: snapshot.ft4_decode_enabled,
|
||||
ft2_decode_enabled: snapshot.ft2_decode_enabled,
|
||||
wspr_decode_enabled: snapshot.wspr_decode_enabled,
|
||||
lrpt_decode_enabled: snapshot.lrpt_decode_enabled,
|
||||
filter: snapshot.filter,
|
||||
spectrum: None, // spectrum flows through /api/spectrum, not persistent state
|
||||
vchan_rds: None, // vchan RDS flows through /api/spectrum, not persistent state
|
||||
aprs_decode_reset_seq: 0,
|
||||
hf_aprs_decode_reset_seq: 0,
|
||||
cw_decode_reset_seq: 0,
|
||||
ft8_decode_reset_seq: 0,
|
||||
ft4_decode_reset_seq: 0,
|
||||
ft2_decode_reset_seq: 0,
|
||||
wspr_decode_reset_seq: 0,
|
||||
|
||||
lrpt_decode_reset_seq: 0,
|
||||
reset_seqs: DecoderResetSeqs::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,17 +271,10 @@ impl RigState {
|
||||
server_longitude: self.server_longitude,
|
||||
pskreporter_status: self.pskreporter_status.clone(),
|
||||
aprs_is_status: self.aprs_is_status.clone(),
|
||||
aprs_decode_enabled: self.aprs_decode_enabled,
|
||||
hf_aprs_decode_enabled: self.hf_aprs_decode_enabled,
|
||||
cw_decode_enabled: self.cw_decode_enabled,
|
||||
decoders: self.decoders.clone(),
|
||||
cw_auto: self.cw_auto,
|
||||
cw_wpm: self.cw_wpm,
|
||||
cw_tone_hz: self.cw_tone_hz,
|
||||
ft8_decode_enabled: self.ft8_decode_enabled,
|
||||
ft4_decode_enabled: self.ft4_decode_enabled,
|
||||
ft2_decode_enabled: self.ft2_decode_enabled,
|
||||
wspr_decode_enabled: self.wspr_decode_enabled,
|
||||
lrpt_decode_enabled: self.lrpt_decode_enabled,
|
||||
filter: self.filter.clone(),
|
||||
spectrum: self.spectrum.clone(),
|
||||
vchan_rds: self.vchan_rds.clone(),
|
||||
@@ -306,7 +291,7 @@ impl RigState {
|
||||
let cw_mode = matches!(mode, RigMode::CW | RigMode::CWR);
|
||||
self.status.mode = mode;
|
||||
if cw_mode {
|
||||
self.cw_decode_enabled = true;
|
||||
self.decoders.cw_decode_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,22 +471,9 @@ pub struct RigSnapshot {
|
||||
pub pskreporter_status: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub aprs_is_status: Option<String>,
|
||||
#[serde(default)]
|
||||
pub aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub hf_aprs_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub cw_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft8_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft4_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub ft2_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub wspr_decode_enabled: bool,
|
||||
#[serde(default)]
|
||||
pub lrpt_decode_enabled: bool,
|
||||
/// Decoder enable/disable flags.
|
||||
#[serde(flatten)]
|
||||
pub decoders: DecoderConfig,
|
||||
#[serde(default)]
|
||||
pub cw_auto: bool,
|
||||
#[serde(default)]
|
||||
|
||||
Reference in New Issue
Block a user