Create rig control interface and port Hamlib implementation to it.

pull/564/head
Mooneer Salem 2023-10-08 01:44:54 -07:00
parent 5ddbdf0563
commit 55872ce43d
12 changed files with 1010 additions and 2 deletions

View File

@ -44,3 +44,4 @@ list(APPEND FREEDV_STATIC_DEPS hamlib)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector")
set(HAMLIB_ADD_DEPENDENCY TRUE)

View File

@ -55,7 +55,9 @@ add_subdirectory(config)
add_subdirectory(gui)
add_subdirectory(pipeline)
add_subdirectory(reporting)
add_subdirectory(rig_control)
add_subdirectory(sox)
add_subdirectory(util)
# WIN32 is needed for Windows GUI apps and is ignored for UNIX like systems.
# In addition, there are some required OSX-specific code files for platform specific handling.
@ -80,9 +82,9 @@ endif(APPLE)
# Link imported or build tree targets.
if(APPLE)
target_link_libraries(FreeDV fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_sox lpcnetfreedv codec2)
target_link_libraries(FreeDV fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_rig_control fdv_sox fdv_util lpcnetfreedv codec2)
else(APPLE)
target_link_libraries(freedv fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_sox lpcnetfreedv codec2)
target_link_libraries(freedv fdv_audio fdv_audio_pipeline fdv_config fdv_gui_util fdv_reporting fdv_rig_control fdv_sox fdv_util lpcnetfreedv codec2)
endif(APPLE)
# Add build dependencies for internally built external libraries.

View File

@ -0,0 +1,9 @@
add_library(fdv_rig_control STATIC
HamlibRigController.cpp
)
target_include_directories(fdv_rig_control PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..)
if(HAMLIB_ADD_DEPENDENCY)
add_dependencies(fdv_rig_control hamlib)
endif(HAMLIB_ADD_DEPENDENCY)

View File

@ -0,0 +1,563 @@
//=========================================================================
// Name: HamlibRigController.cpp
// Purpose: Controls radios using Hamlib library.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#include <mutex>
#include <condition_variable>
#include <functional>
#include <sstream>
#include "HamlibRigController.h"
extern int g_verbose;
HamlibRigController::RigList HamlibRigController::RigList_;
std::mutex HamlibRigController::RigListMutex_;
int HamlibRigController::BuildRigList_(const struct rig_caps *rig, rig_ptr_t rigList) {
((HamlibRigController::RigList *)rigList)->push_back(rig);
return 1;
}
bool HamlibRigController::RigCompare_(const struct rig_caps *rig1, const struct rig_caps *rig2) {
/* Compare manufacturer. */
int r = strcasecmp(rig1->mfg_name, rig2->mfg_name);
if (r != 0)
return r < 0;
/* Compare model. */
r = strcasecmp(rig1->model_name, rig2->model_name);
if (r != 0)
return r < 0;
/* Compare rig ID. */
return rig1->rig_model < rig2->rig_model;
}
HamlibRigController::HamlibRigController(std::string rigName, std::string serialPort, const int serialRate, const int civHex, const PttType pttType, std::string pttSerialPort)
: rigName_(rigName)
, serialPort_(serialPort)
, serialRate_(serialRate)
, civHex_(civHex)
, pttType_(pttType)
, pttSerialPort_(pttSerialPort)
, rig_(nullptr)
, multipleVfos_(false)
, pttSet_(false)
{
// Perform initial load of rig list if this is our first time being created.
std::unique_lock<std::mutex> lk(RigListMutex_);
if (RigList_.size() == 0)
{
/* Stop hamlib from spewing info to stderr. */
rig_set_debug(RIG_DEBUG_NONE);
/* Create sorted list of rigs. */
rig_load_all_backends();
rig_list_foreach(&HamlibRigController::BuildRigList_, &RigList_);
sort(RigList_.begin(), RigList_.end(), &HamlibRigController::RigCompare_);
/* Reset debug output. */
rig_set_debug(RIG_DEBUG_VERBOSE);
}
}
HamlibRigController::~HamlibRigController()
{
// Disconnect in a synchronous fashion before killing our thread.
std::condition_variable cv;
std::mutex mtx;
std::unique_lock<std::mutex> lk(mtx);
enqueue_([&]() {
std::unique_lock<std::mutex> innerLock(mtx);
if (rig_ != nullptr)
{
disconnectImpl_();
}
cv.notify_one();
});
cv.wait(lk);
}
void HamlibRigController::connect()
{
enqueue_(std::bind(&HamlibRigController::connectImpl_, this));
}
void HamlibRigController::disconnect()
{
enqueue_(std::bind(&HamlibRigController::disconnectImpl_, this));
}
void HamlibRigController::ptt(bool state)
{
enqueue_(std::bind(&HamlibRigController::pttImpl_, this, state));
}
void HamlibRigController::setFrequency(uint64_t frequency)
{
enqueue_(std::bind(&HamlibRigController::setFrequencyImpl_, this, frequency));
}
void HamlibRigController::setMode(IRigFrequencyController::Mode mode)
{
enqueue_(std::bind(&HamlibRigController::setModeImpl_, this, mode));
}
void HamlibRigController::requestCurrentFrequencyMode()
{
enqueue_(std::bind(&HamlibRigController::requestCurrentFrequencyModeImpl_, this));
}
unsigned int HamlibRigController::rigNameToIndex_(std::string rigName)
{
unsigned int index = 0;
for (auto& entry : RigList_)
{
char name[128];
snprintf(name, 128, "%s %s", entry->mfg_name, entry->model_name);
if (rigName == std::string(name))
{
return index;
}
index++;
}
return -1;
}
// Note: these execute in the thread created by ThreadedObject on object instantiation.
void HamlibRigController::connectImpl_()
{
if (rig_ != nullptr)
{
std::string errMsg = "Already connected to radio.";
onRigError(this, errMsg);
return;
}
auto rigIndex = rigNameToIndex_(rigName_);
if (rigIndex == -1)
{
std::string errMsg = "Could not find rig " + rigName_ + " in Hamlib database.";
onRigError(this, errMsg);
return;
}
/* Initialise, configure and open. */
rig_ = rig_init(RigList_[rigIndex]->rig_model);
if (!rig_)
{
if (g_verbose) fprintf(stderr, "rig_init() failed, returning\n");
std::string errMsg = "Hamlib could not initialize rig " + rigName_;
onRigError(this, errMsg);
return;
}
if (g_verbose) fprintf(stderr, "rig_init() OK ....\n");
/* Set CI-V address if necessary. */
if (!strncmp(RigList_[rigIndex]->mfg_name, "Icom", 4))
{
char civAddr[5];
snprintf(civAddr, 5, "0x%0X", civHex_);
if (g_verbose) fprintf(stderr, "hamlib: setting CI-V address to: %s\n", civAddr);
rig_set_conf(rig_, rig_token_lookup(rig_, "civaddr"), civAddr);
}
else
{
if (g_verbose) fprintf(stderr, "hamlib: ignoring CI-V configuration due to non-Icom radio\r\n");
}
rig_set_conf(rig_, rig_token_lookup(rig_, "rig_pathname"), serialPort_.c_str());
if (pttSerialPort_.size() > 0)
{
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_pathname"), pttSerialPort_.c_str());
}
if (serialRate_ > 0)
{
if (g_verbose) fprintf(stderr, "hamlib: setting serial rate: %d\n", serialRate_);
std::stringstream ss;
ss << serialRate_;
rig_set_conf(rig_, rig_token_lookup(rig_, "serial_speed"), ss.str().c_str());
}
// Set PTT type as needed.
switch(pttType_)
{
case PTT_VIA_RTS:
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "RTS");
break;
case PTT_VIA_DTR:
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "DTR");
break;
case PTT_VIA_NONE:
rig_set_conf(rig_, rig_token_lookup(rig_, "ptt_type"), "NONE");
break;
case PTT_VIA_CAT:
default:
break;
}
if (rig_open(rig_) == RIG_OK)
{
if (g_verbose) fprintf(stderr, "hamlib: rig_open() OK\n");
onRigConnected(this);
// Determine whether we have multiple VFOs.
multipleVfos_ = false;
vfo_t vfo;
if (rig_get_vfo (rig_, &vfo) == RIG_OK && (rig_->state.vfo_list & RIG_VFO_B))
{
multipleVfos_ = true;
}
// Get current frequency and mode when we first connect so we can
// revert on close.
requestCurrentFrequencyMode();
return;
}
if (g_verbose) fprintf(stderr, "hamlib: rig_open() failed ...\n");
rig_cleanup(rig_);
rig_ = nullptr;
}
void HamlibRigController::disconnectImpl_()
{
if (rig_)
{
// Stop transmitting.
if (pttSet_)
{
pttImpl_(false);
}
rig_close(rig_);
rig_cleanup(rig_);
rig_ = nullptr;
}
}
void HamlibRigController::pttImpl_(bool state)
{
if (g_verbose) fprintf(stderr,"Hamlib::ptt: %d\n", state);
if (rig_ == nullptr)
{
onRigError(this, "Cannot set PTT: not connected to radio");
return;
}
ptt_t on = state ? RIG_PTT_ON : RIG_PTT_OFF;
int result = rig_set_ptt(rig_, RIG_VFO_CURR, on);
if (result != RIG_OK)
{
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
std::string errMsg = "Cannot set PTT: " + std::string(rigerror(result));
onRigError(this, errMsg);
}
else
{
if (pttSet_ != state)
{
onPttChange(this, state);
}
pttSet_ = state;
if (!state)
{
requestCurrentFrequencyMode();
}
}
}
void HamlibRigController::setFrequencyImpl_(uint64_t frequencyHz)
{
if (rig_ == nullptr)
{
onRigError(this, "Cannot get current frequency/mode: not connected to radio");
return;
}
if (pttSet_)
{
// If transmitting, temporarily stop transmitting so we can change the mode.
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_OFF);
if (result != RIG_OK)
{
// If we can't stop transmitting, we shouldn't try to change the mode
// as it'll fail on some radios.
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
return;
}
}
vfo_t currVfo = getCurrentVfo_();
setFrequencyHelper_(currVfo, frequencyHz);
if (pttSet_)
{
// If transmitting, temporarily stop transmitting so we can change the mode.
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_ON);
if (result != RIG_OK)
{
// If we can't stop transmitting, we shouldn't try to change the mode
// as it'll fail on some radios.
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
}
}
}
void HamlibRigController::setModeImpl_(IRigFrequencyController::Mode mode)
{
rmode_t hamlibMode;
switch (mode)
{
case USB:
hamlibMode = RIG_MODE_USB;
break;
case LSB:
hamlibMode = RIG_MODE_LSB;
break;
case DIGU:
hamlibMode = RIG_MODE_PKTUSB;
break;
case DIGL:
hamlibMode = RIG_MODE_PKTLSB;
break;
case FM:
hamlibMode = RIG_MODE_FM;
break;
case DIGFM:
hamlibMode = RIG_MODE_PKTFM;
break;
case AM:
hamlibMode = RIG_MODE_AM;
break;
default:
onRigError(this, "Cannot set mode: mode not recognized");
return;
}
if (pttSet_)
{
// If transmitting, temporarily stop transmitting so we can change the mode.
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_OFF);
if (result != RIG_OK)
{
// If we can't stop transmitting, we shouldn't try to change the mode
// as it'll fail on some radios.
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
return;
}
}
vfo_t currVfo = getCurrentVfo_();
setModeHelper_(currVfo, hamlibMode);
if (pttSet_)
{
// If transmitting, temporarily stop transmitting so we can change the mode.
int result = rig_set_ptt(rig_, RIG_VFO_CURR, RIG_PTT_ON);
if (result != RIG_OK)
{
// If we can't stop transmitting, we shouldn't try to change the mode
// as it'll fail on some radios.
if (g_verbose) fprintf(stderr, "rig_set_ptt: error = %s \n", rigerror(result));
}
}
}
void HamlibRigController::requestCurrentFrequencyModeImpl_()
{
if (rig_ == nullptr)
{
onRigError(this, "Cannot get current frequency/mode: not connected to radio");
return;
}
rmode_t mode = RIG_MODE_NONE;
pbwidth_t passband = 0;
vfo_t currVfo = getCurrentVfo_();
modeAttempt:
int result = rig_get_mode(rig_, currVfo, &mode, &passband);
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
{
if (g_verbose) fprintf(stderr, "rig_get_mode: error = %s \n", rigerror(result));
}
else if (result != RIG_OK)
{
// We supposedly have multiple VFOs but ran into problems
// trying to get information about the supposed active VFO.
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
currVfo = RIG_VFO_CURR;
goto modeAttempt;
}
else
{
freqAttempt:
freq_t freq = 0;
result = rig_get_freq(rig_, currVfo, &freq);
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
{
if (g_verbose) fprintf(stderr, "rig_get_freq: error = %s \n", rigerror(result));
}
else if (result != RIG_OK)
{
// We supposedly have multiple VFOs but ran into problems
// trying to get information about the supposed active VFO.
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
currVfo = RIG_VFO_CURR;
goto freqAttempt;
}
else
{
IRigFrequencyController::Mode currMode;
switch (mode)
{
case RIG_MODE_USB:
currMode = USB;
break;
case RIG_MODE_LSB:
currMode = LSB;
break;
case RIG_MODE_PKTUSB:
currMode = DIGU;
break;
case RIG_MODE_PKTLSB:
currMode = DIGL;
break;
case RIG_MODE_FM:
currMode = FM;
break;
case RIG_MODE_PKTFM:
currMode = DIGFM;
break;
case RIG_MODE_AM:
currMode = AM;
break;
default:
currMode = UNKNOWN;
break;
}
onFreqModeChange(this, freq, currMode);
}
}
}
vfo_t HamlibRigController::getCurrentVfo_()
{
vfo_t vfo = RIG_VFO_CURR;
if (multipleVfos_)
{
int result = rig_get_vfo(rig_, &vfo);
if (result != RIG_OK && result != -RIG_ENAVAIL)
{
// Note: we'll still attempt operation using RIG_VFO_CURR just in case.
if (g_verbose) fprintf(stderr, "rig_get_vfo: error = %s \n", rigerror(result));
vfo = RIG_VFO_CURR;
}
}
return vfo;
}
void HamlibRigController::setFrequencyHelper_(vfo_t currVfo, uint64_t frequencyHz)
{
bool setOkay = false;
freqAttempt:
int result = rig_set_freq(rig_, currVfo, frequencyHz);
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
{
if (g_verbose) fprintf(stderr, "rig_set_freq: error = %s \n", rigerror(result));
}
else if (result != RIG_OK)
{
// We supposedly have multiple VFOs but ran into problems
// trying to get information about the supposed active VFO.
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
currVfo = RIG_VFO_CURR;
goto freqAttempt;
}
else
{
setOkay = true;
}
// If we're able to set either frequency or mode, we should update the current state accordingly.
// The actual configuration of the radio (in case of failure) will auto-update within
// a few seconds after this update.
if (setOkay)
{
requestCurrentFrequencyMode();
}
}
void HamlibRigController::setModeHelper_(vfo_t currVfo, rmode_t mode)
{
bool setOkay = false;
modeAttempt:
int result = rig_set_mode(rig_, currVfo, mode, RIG_PASSBAND_NOCHANGE);
if (result != RIG_OK && currVfo == RIG_VFO_CURR)
{
if (g_verbose) fprintf(stderr, "rig_set_mode: error = %s \n", rigerror(result));
}
else if (result != RIG_OK)
{
// We supposedly have multiple VFOs but ran into problems
// trying to get information about the supposed active VFO.
// Make a last ditch effort using RIG_VFO_CURR before fully failing.
currVfo = RIG_VFO_CURR;
goto modeAttempt;
}
else
{
setOkay = true;
}
// If we're able to set either frequency or mode, we should update the current state accordingly.
// The actual configuration of the radio (in case of failure) will auto-update within
// a few seconds after this update.
if (setOkay)
{
requestCurrentFrequencyMode();
}
}

View File

@ -0,0 +1,93 @@
//=========================================================================
// Name: HamlibRigController.h
// Purpose: Controls radios using Hamlib library.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef HAMLIB_RIG_CONTROLLER_H
#define HAMLIB_RIG_CONTROLLER_H
#include <string>
#include <vector>
#include <mutex>
#include "util/ThreadedObject.h"
#include "IRigFrequencyController.h"
#include "IRigPttController.h"
extern "C"
{
#include <hamlib/rig.h>
}
class HamlibRigController : public ThreadedObject, public IRigFrequencyController, public IRigPttController
{
public:
enum PttType
{
PTT_VIA_CAT = 0,
PTT_VIA_RTS,
PTT_VIA_DTR,
PTT_VIA_NONE,
};
HamlibRigController(std::string rigName, std::string serialPort, const int serialRate, const int civHex = 0, const PttType pttType = PTT_VIA_CAT, std::string pttSerialPort = std::string());
virtual ~HamlibRigController();
virtual void connect() override;
virtual void disconnect() override;
virtual void ptt(bool state) override;
virtual void setFrequency(uint64_t frequency) override;
virtual void setMode(IRigFrequencyController::Mode mode) override;
virtual void requestCurrentFrequencyMode() override;
private:
using RigList = std::vector<const struct rig_caps *>;
std::string rigName_;
std::string serialPort_;
const int serialRate_;
const int civHex_;
const PttType pttType_;
std::string pttSerialPort_;
RIG* rig_;
bool multipleVfos_;
bool pttSet_;
unsigned int rigNameToIndex_(std::string rigName);
vfo_t getCurrentVfo_();
void setFrequencyHelper_(vfo_t currVfo, uint64_t frequencyHz);
void setModeHelper_(vfo_t currVfo, rmode_t mode);
void connectImpl_();
void disconnectImpl_();
void pttImpl_(bool state);
void setFrequencyImpl_(uint64_t frequencyHz);
void setModeImpl_(IRigFrequencyController::Mode mode);
void requestCurrentFrequencyModeImpl_();
static RigList RigList_;
static std::mutex RigListMutex_;
static bool RigCompare_(const struct rig_caps *rig1, const struct rig_caps *rig2);
static int BuildRigList_(const struct rig_caps *rig, rig_ptr_t);
};
#endif // HAMLIB_RIG_CONTROLLER_H

View File

@ -0,0 +1,46 @@
//=========================================================================
// Name: IRigController.h
// Purpose: Interface for control of radios.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef I_RIG_CONTROLLER_H
#define I_RIG_CONTROLLER_H
#include <string>
#include "util/EventHandler.h"
class IRigController
{
public:
virtual ~IRigController() = default;
// Event handlers common across all rig control objects.
EventHandler<IRigController*, std::string> onRigError;
EventHandler<IRigController*> onRigConnected;
EventHandler<IRigController*> onRigDisconnected;
virtual void connect() = 0;
virtual void disconnect() = 0;
protected:
IRigController() = default;
};
#endif // I_RIG_CONTROLLER_H

View File

@ -0,0 +1,57 @@
//=========================================================================
// Name: IRigFrequencyController.h
// Purpose: Interface for control of frequency/mode on radios.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef I_RIG_FREQUENCY_CONTROLLER_H
#define I_RIG_FREQUENCY_CONTROLLER_H
#include "IRigController.h"
class IRigFrequencyController : virtual public IRigController
{
public:
enum Mode
{
UNKNOWN,
USB,
DIGU,
LSB,
DIGL,
FM,
DIGFM,
AM,
};
virtual ~IRigFrequencyController() = default;
// Event handlers that are called by implementers of this interface.
EventHandler<IRigFrequencyController*, uint64_t, Mode> onFreqModeChange;
virtual void setFrequency(uint64_t frequency) = 0;
virtual void setMode(Mode mode) = 0;
virtual void requestCurrentFrequencyMode() = 0;
protected:
IRigFrequencyController() = default;
};
#endif // I_RIG_FREQUENCY_CONTROLLER_H

View File

@ -0,0 +1,42 @@
//=========================================================================
// Name: IRigPttController.h
// Purpose: Interface for control of PTT on radios.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef I_RIG_PTT_CONTROLLER_H
#define I_RIG_PTT_CONTROLLER_H
#include "IRigController.h"
class IRigPttController : virtual public IRigController
{
public:
virtual ~IRigPttController() = default;
// Event handlers that are called by implementers of this interface.
EventHandler<IRigPttController*, bool> onPttChange;
virtual void ptt(bool state) = 0;
protected:
IRigPttController() = default;
};
#endif // I_RIG_PTT_CONTROLLER_H

View File

@ -0,0 +1,3 @@
add_library(fdv_util STATIC
ThreadedObject.cpp
)

View File

@ -0,0 +1,68 @@
//=========================================================================
// Name: EventHandler.h
// Purpose: List of functions that handle an event
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef EVENT_HANDLER_H
#define EVENT_HANDLER_H
#include <functional>
#include <vector>
template<typename... FnArgs>
class EventHandler
{
public:
using FnType = std::function<void(FnArgs...)>;
EventHandler() = default;
virtual ~EventHandler() = default;
void operator() (FnArgs... args);
EventHandler<FnArgs...>& operator+=(FnType fn) const;
void clear();
private:
std::vector<FnType> fnList_;
};
template<typename... FnArgs>
void EventHandler<FnArgs...>::operator() (FnArgs... args)
{
for (auto& fn : fnList_)
{
fn(args...);
}
}
template<typename... FnArgs>
EventHandler<FnArgs...>& EventHandler<FnArgs...>::operator+=(FnType fn) const
{
fnList_.push_back(fn);
return *this;
}
template<typename... FnArgs>
void EventHandler<FnArgs...>::clear()
{
fnList_.clear();
}
#endif // EVENT_HANDLER_H

View File

@ -0,0 +1,72 @@
//=========================================================================
// Name: ThreadedObject.cpp
// Purpose: Wrapper to allow class to execute in its own thread.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#include "ThreadedObject.h"
ThreadedObject::ThreadedObject()
: isDestroying_(false),
objectThread_(std::bind(&ThreadedObject::eventLoop_, this))
{
// empty
}
ThreadedObject::~ThreadedObject()
{
isDestroying_ = true;
eventQueueCV_.notify_one();
objectThread_.join();
}
void ThreadedObject::enqueue_(std::function<void()> fn)
{
std::unique_lock<std::mutex> lk(eventQueueMutex_);
eventQueue_.push_back(fn);
eventQueueCV_.notify_one();
}
void ThreadedObject::eventLoop_()
{
while (!isDestroying_)
{
std::function<void()> fn;
{
std::unique_lock<std::mutex> lk(eventQueueMutex_);
eventQueueCV_.wait(lk, [&]() {
return isDestroying_ || eventQueue_.size() > 0;
});
if (isDestroying_)
{
break;
}
fn = eventQueue_[0];
eventQueue_.erase(eventQueue_.begin());
}
if (fn)
{
fn();
}
}
}

View File

@ -0,0 +1,52 @@
//=========================================================================
// Name: ThreadedObject.h
// Purpose: Wrapper to allow class to execute in its own thread.
//
// Authors: Mooneer Salem
// License:
//
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.1,
// as published by the Free Software Foundation. This program is
// distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, see <http://www.gnu.org/licenses/>.
//
//=========================================================================
#ifndef THREADED_OBJECT_H
#define THREADED_OBJECT_H
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <functional>
class ThreadedObject
{
public:
virtual ~ThreadedObject();
protected:
ThreadedObject();
void enqueue_(std::function<void()> fn);
private:
bool isDestroying_;
std::thread objectThread_;
std::vector<std::function<void()> > eventQueue_;
std::mutex eventQueueMutex_;
std::condition_variable eventQueueCV_;
void eventLoop_();
};
#endif // THREADED_OBJECT_H