js8call/JS8_Transceiver/TransceiverBase.h
2026-01-18 19:19:15 -06:00

164 lines
5.8 KiB
C++

#ifndef TRANSCEIVER_BASE_HPP__
#define TRANSCEIVER_BASE_HPP__
#include "Transceiver.h"
#include <QLoggingCategory>
#include <QString>
#include <stdexcept>
//
// Base Transceiver Implementation
//
// Behaviour common to all Transceiver implementations.
//
// Collaborations
//
// Implements the Transceiver abstract interface as template methods
// and provides a new abstract interface with similar functionality
// (do_XXXXX operations). Provides and calls abstract interface that
// gets called post the above operations (do_post_XXXXX) to allow
// caching implementation etc.
//
// A key factor is to catch all exceptions thrown by sub-class
// implementations where the template method is a Qt slot which is
// therefore likely to be called by Qt which doesn't handle
// exceptions. Any exceptions are converted to Transceiver::failure()
// signals.
//
// Sub-classes update the stored state via a protected interface.
//
// Responsibilities:
//
// Wrap incoming Transceiver messages catching all exceptions in Qt
// slot driven messages and converting them to Qt signals. This is
// done because exceptions make concrete Transceiver implementations
// simpler to write, but exceptions cannot cross signal/slot
// boundaries (especially across threads). This also removes any
// requirement for the client code to handle exceptions.
//
// Maintain the state of the concrete Transceiver instance that is
// passed back via the Transceiver::update(TransceiverState) signal,
// it is still the responsibility of concrete Transceiver
// implementations to emit the state_change signal when they have a
// status update.
//
// Maintain a go/no-go status for concrete Transceiver
// implementations ensuring only a valid sequence of messages are
// passed. A concrete Transceiver instance must be started before it
// can receive messages, any exception thrown takes the Transceiver
// offline.
//
// Implements methods that concrete Transceiver implementations use
// to update the Transceiver state. These do not signal state change
// to clients as this is the responsibility of the concrete
// Transceiver implementation, thus allowing multiple state component
// updates to be signalled together if required.
//
class TransceiverBase : public Transceiver {
Q_OBJECT;
protected:
TransceiverBase(QObject *parent)
: Transceiver{parent}, last_sequence_number_{0} {}
public:
//
// Implement the Transceiver abstract interface.
//
void start(unsigned sequence_number) noexcept override final;
void set(TransceiverState const &,
unsigned sequence_number) noexcept override final;
void stop() noexcept override final;
//
// Query operations
//
TransceiverState const &state() const { return actual_; }
protected:
//
// Error exception which is thrown to signal unexpected errors.
//
struct error : public std::runtime_error {
explicit error(char const *const msg) : std::runtime_error(msg) {}
explicit error(QString const &msg)
: std::runtime_error(msg.toStdString()) {}
};
// Template methods that sub classes implement to do what they need to do.
//
// These methods may throw exceptions to signal errors.
virtual int
do_start() = 0; // returns resolution, See Transceiver::resolution
virtual void do_post_start() {}
virtual void do_stop() = 0;
virtual void do_post_stop() {}
virtual void do_frequency(Frequency, MODE, bool no_ignore) = 0;
virtual void do_post_frequency(Frequency, MODE) {}
/** Work around hamlib bug
* [#1966](https://github.com/Hamlib/Hamlib/issues/1966). */
inline virtual void hamlib_bug_bandaid(TransceiverState const &) {};
virtual void do_tx_frequency(Frequency, MODE, bool no_ignore) = 0;
virtual void do_post_tx_frequency(Frequency, MODE) {}
virtual void do_mode(MODE) = 0;
virtual void do_post_mode(MODE) {}
virtual void do_ptt(bool = true) = 0;
virtual void do_post_ptt(bool = true) {}
virtual void do_sync(bool force_signal = false, bool no_poll = false) = 0;
virtual bool do_pre_update() { return true; }
// sub classes report rig state changes with these methods
void update_rx_frequency(Frequency);
void update_other_frequency(Frequency = 0);
void update_split(bool);
void update_mode(MODE);
void update_PTT(bool = true);
// Calling this eventually triggers the Transceiver::update(State) signal.
void update_complete(bool force_signal = false);
// sub class may asynchronously take the rig offline by calling this
void offline(QString const &reason);
private:
void startup();
void shutdown();
bool maybe_low_resolution(Frequency low_res, Frequency high_res);
// use this convenience class to notify in update methods
class may_update {
public:
explicit may_update(TransceiverBase *self, bool force_signal = false)
: self_{self}, force_signal_{force_signal} {}
~may_update() { self_->update_complete(force_signal_); }
private:
TransceiverBase *self_;
bool force_signal_;
};
TransceiverState requested_;
TransceiverState actual_;
TransceiverState last_;
unsigned last_sequence_number_; // from set state operation
};
// some trace macros
Q_DECLARE_LOGGING_CATEGORY(transceiverbase_js8)
#define TRACE_CAT(FAC, MSG) \
qCDebug(transceiverbase_js8) \
<< QString{"%1::%2:"}.arg((FAC)).arg(__func__) << MSG
#define TRACE_CAT_POLL(FAC, MSG) \
qCDebug(transceiverbase_js8) \
<< QString{"%1::%2:"}.arg((FAC)).arg(__func__) << MSG
#endif