mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-26 22:28:41 -05:00
210 lines
4.9 KiB
C++
210 lines
4.9 KiB
C++
|
#include "PollingTransceiver.hpp"
|
||
|
|
||
|
#include <exception>
|
||
|
|
||
|
#include <QObject>
|
||
|
#include <QString>
|
||
|
#include <QTimer>
|
||
|
|
||
|
#include "pimpl_impl.hpp"
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
unsigned const polls_to_stabilize {3};
|
||
|
}
|
||
|
|
||
|
// Internal implementation of the PollingTransceiver class.
|
||
|
class PollingTransceiver::impl final
|
||
|
: public QObject
|
||
|
{
|
||
|
Q_OBJECT;
|
||
|
|
||
|
private:
|
||
|
Q_DISABLE_COPY (impl);
|
||
|
|
||
|
public:
|
||
|
impl (PollingTransceiver * self, int poll_interval)
|
||
|
: QObject {self}
|
||
|
, self_ {self}
|
||
|
, interval_ {poll_interval}
|
||
|
, poll_timer_ {nullptr}
|
||
|
, retries_ {0}
|
||
|
{
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void start_timer ()
|
||
|
{
|
||
|
if (interval_)
|
||
|
{
|
||
|
if (!poll_timer_)
|
||
|
{
|
||
|
poll_timer_ = new QTimer {this}; // pass ownership to QObject which handles destruction for us
|
||
|
|
||
|
connect (poll_timer_, &QTimer::timeout, this, &PollingTransceiver::impl::handle_timeout);
|
||
|
}
|
||
|
poll_timer_->start (interval_);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void stop_timer ()
|
||
|
{
|
||
|
if (poll_timer_)
|
||
|
{
|
||
|
poll_timer_->stop ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Q_SLOT void handle_timeout ();
|
||
|
|
||
|
PollingTransceiver * self_; // our owner so we can call methods
|
||
|
int interval_; // polling interval in milliseconds
|
||
|
QTimer * poll_timer_;
|
||
|
|
||
|
// keep a record of the last state signalled so we can elide
|
||
|
// duplicate updates
|
||
|
Transceiver::TransceiverState last_signalled_state_;
|
||
|
|
||
|
// keep a record of expected state so we can compare with actual
|
||
|
// updates to determine when state changes have bubbled through
|
||
|
Transceiver::TransceiverState next_state_;
|
||
|
|
||
|
unsigned retries_; // number of incorrect polls left
|
||
|
|
||
|
friend class PollingTransceiver;
|
||
|
};
|
||
|
|
||
|
#include "PollingTransceiver.moc"
|
||
|
|
||
|
|
||
|
PollingTransceiver::PollingTransceiver (int poll_interval)
|
||
|
: m_ {this, poll_interval}
|
||
|
{
|
||
|
}
|
||
|
|
||
|
PollingTransceiver::~PollingTransceiver ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_post_start ()
|
||
|
{
|
||
|
m_->start_timer ();
|
||
|
if (!m_->next_state_.online ())
|
||
|
{
|
||
|
// remember that we are expecting to go online
|
||
|
m_->next_state_.online (true);
|
||
|
m_->retries_ = polls_to_stabilize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_post_stop ()
|
||
|
{
|
||
|
// not much point waiting for rig to go offline since we are ceasing
|
||
|
// polls
|
||
|
m_->stop_timer ();
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_post_frequency (Frequency f)
|
||
|
{
|
||
|
if (m_->next_state_.frequency () != f)
|
||
|
{
|
||
|
// update expected state with new frequency and set poll count
|
||
|
m_->next_state_.frequency (f);
|
||
|
m_->retries_ = polls_to_stabilize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_post_tx_frequency (Frequency f)
|
||
|
{
|
||
|
if (m_->next_state_.tx_frequency () != f)
|
||
|
{
|
||
|
// update expected state with new TX frequency and set poll
|
||
|
// count
|
||
|
m_->next_state_.tx_frequency (f);
|
||
|
m_->next_state_.split (f); // setting non-zero TX frequency means split
|
||
|
m_->retries_ = polls_to_stabilize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_post_mode (MODE m)
|
||
|
{
|
||
|
if (m_->next_state_.mode () != m)
|
||
|
{
|
||
|
// update expected state with new mode and set poll count
|
||
|
m_->next_state_.mode (m);
|
||
|
m_->retries_ = polls_to_stabilize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool PollingTransceiver::do_pre_update ()
|
||
|
{
|
||
|
// if we are holding off a change then withhold the signal
|
||
|
if (m_->retries_ && state () != m_->next_state_)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::do_sync (bool force_signal)
|
||
|
{
|
||
|
poll (); // tell sub-classes to update our
|
||
|
// state
|
||
|
|
||
|
// Signal new state if it is directly requested or, what we expected
|
||
|
// or, hasn't become what we expected after polls_to_stabilize
|
||
|
// polls. Unsolicited changes will be signalled immediately unless
|
||
|
// they intervene in a expected sequence where they will be delayed.
|
||
|
if (m_->retries_)
|
||
|
{
|
||
|
--m_->retries_;
|
||
|
if (force_signal || state () == m_->next_state_ || !m_->retries_)
|
||
|
{
|
||
|
// our client wants a signal regardless
|
||
|
// or the expected state has arrived
|
||
|
// or there are no more retries
|
||
|
force_signal = true;
|
||
|
}
|
||
|
}
|
||
|
else if (force_signal || state () != m_->last_signalled_state_)
|
||
|
{
|
||
|
// here is the normal passive polling path
|
||
|
// either our client has requested a state update regardless of change
|
||
|
// or sate has changed asynchronously
|
||
|
force_signal = true;
|
||
|
}
|
||
|
|
||
|
if (force_signal)
|
||
|
{
|
||
|
// reset everything, record and signal the current state
|
||
|
m_->retries_ = 0;
|
||
|
m_->next_state_ = state ();
|
||
|
m_->last_signalled_state_ = state ();
|
||
|
update_complete ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PollingTransceiver::impl::handle_timeout ()
|
||
|
{
|
||
|
QString message;
|
||
|
|
||
|
// we must catch all exceptions here since we are called by Qt and
|
||
|
// inform our parent of the failure via the offline() message
|
||
|
try
|
||
|
{
|
||
|
self_->do_sync (false);
|
||
|
}
|
||
|
catch (std::exception const& e)
|
||
|
{
|
||
|
message = e.what ();
|
||
|
}
|
||
|
catch (...)
|
||
|
{
|
||
|
message = "Unexpected rig error";
|
||
|
}
|
||
|
if (!message.isEmpty ())
|
||
|
{
|
||
|
self_->offline (message);
|
||
|
}
|
||
|
}
|