mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-01-11 18:57:11 +01:00
Beginning of scheduler code
This commit is contained in:
parent
d20b41401f
commit
0ab4d16f9d
@ -45,6 +45,7 @@ option(OPT_BUILD_FREQUENCY_MANAGER "Build the Frequency Manager module" ON)
|
||||
option(OPT_BUILD_RECORDER "Audio and baseband recorder" ON)
|
||||
option(OPT_BUILD_RIGCTL_SERVER "Rigctl backend for controlling SDR++ with software like gpredict" ON)
|
||||
option(OPT_BUILD_SCANNER "Frequency scanner" OFF)
|
||||
option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
|
||||
|
||||
# Other options
|
||||
option(USE_INTERNAL_LIBCORRECT "Use an external version of libcorrect" ON)
|
||||
@ -171,6 +172,10 @@ if (OPT_BUILD_SCANNER)
|
||||
add_subdirectory("misc_modules/scanner")
|
||||
endif (OPT_BUILD_SCANNER)
|
||||
|
||||
if (OPT_BUILD_SCHEDULER)
|
||||
add_subdirectory("misc_modules/scheduler")
|
||||
endif (OPT_BUILD_SCHEDULER)
|
||||
|
||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||
target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
||||
|
||||
@ -208,7 +213,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||
endif ()
|
||||
|
||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_SCANNER=ON -DOPT_BUILD_SCHEDULER=ON
|
||||
|
||||
# Install directives
|
||||
install(TARGETS sdrpp DESTINATION bin)
|
||||
|
35
core/src/utils/freq_formatting.h
Normal file
35
core/src/utils/freq_formatting.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace utils {
|
||||
std::string formatFreq(double freq) {
|
||||
char str[128];
|
||||
if (freq >= 1000000.0) {
|
||||
sprintf(str, "%.06lf", freq / 1000000.0);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "MHz";
|
||||
}
|
||||
else if (freq >= 1000.0) {
|
||||
sprintf(str, "%.06lf", freq / 1000.0);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "KHz";
|
||||
}
|
||||
else {
|
||||
sprintf(str, "%.06lf", freq);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "Hz";
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include <gui/tuner.h>
|
||||
#include <gui/file_dialogs.h>
|
||||
#include <utils/freq_formatting.h>
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "frequency_manager",
|
||||
@ -102,36 +103,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string freqToStr(double freq) {
|
||||
char str[128];
|
||||
if (freq >= 1000000.0) {
|
||||
sprintf(str, "%.06lf", freq / 1000000.0);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "MHz";
|
||||
}
|
||||
else if (freq >= 1000.0) {
|
||||
sprintf(str, "%.06lf", freq / 1000.0);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "KHz";
|
||||
}
|
||||
else {
|
||||
sprintf(str, "%.06lf", freq);
|
||||
int len = strlen(str) - 1;
|
||||
while ((str[len] == '0' || str[len] == '.') && len > 0) {
|
||||
len--;
|
||||
if (str[len] == '.') { len--; break; }
|
||||
}
|
||||
return std::string(str).substr(0, len + 1) + "Hz";
|
||||
}
|
||||
}
|
||||
|
||||
static void applyBookmark(FrequencyBookmark bm, std::string vfoName) {
|
||||
if (vfoName == "") {
|
||||
@ -525,7 +496,7 @@ private:
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("%s %s", freqToStr(bm.frequency).c_str(), demodModeList[bm.mode]);
|
||||
ImGui::Text("%s %s", utils::formatFreq(bm.frequency).c_str(), demodModeList[bm.mode]);
|
||||
ImVec2 max = ImGui::GetCursorPos();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
@ -756,8 +727,8 @@ private:
|
||||
ImGui::Text(hoveredBookmarkName.c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::Text("List: %s", hoveredBookmark.listName.c_str());
|
||||
ImGui::Text("Frequency: %s", freqToStr(hoveredBookmark.bookmark.frequency).c_str());
|
||||
ImGui::Text("Bandwidth: %s", freqToStr(hoveredBookmark.bookmark.bandwidth).c_str());
|
||||
ImGui::Text("Frequency: %s", utils::formatFreq(hoveredBookmark.bookmark.frequency).c_str());
|
||||
ImGui::Text("Bandwidth: %s", utils::formatFreq(hoveredBookmark.bookmark.bandwidth).c_str());
|
||||
ImGui::Text("Mode: %s", demodModeList[hoveredBookmark.bookmark.mode]);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
21
misc_modules/scheduler/CMakeLists.txt
Normal file
21
misc_modules/scheduler/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(scheduler)
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
add_library(scheduler SHARED ${SRC})
|
||||
target_link_libraries(scheduler PRIVATE sdrpp_core)
|
||||
set_target_properties(scheduler PROPERTIES PREFIX "")
|
||||
|
||||
target_include_directories(scheduler PRIVATE "src/")
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(scheduler PRIVATE /O2 /Ob2 /std:c++17 /EHsc)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
target_compile_options(scheduler PRIVATE -O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup)
|
||||
else ()
|
||||
target_compile_options(scheduler PRIVATE -O3 -std=c++17)
|
||||
endif ()
|
||||
|
||||
# Install directives
|
||||
install(TARGETS scheduler DESTINATION lib/sdrpp/plugins)
|
46
misc_modules/scheduler/src/actions/start_recorder.h
Normal file
46
misc_modules/scheduler/src/actions/start_recorder.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <sched_action.h>
|
||||
|
||||
namespace sched_action {
|
||||
class StartRecorderClass : public ActionClass {
|
||||
public:
|
||||
StartRecorderClass() {}
|
||||
~StartRecorderClass() {}
|
||||
|
||||
void trigger() {
|
||||
|
||||
}
|
||||
|
||||
void showEditMenu() {
|
||||
|
||||
}
|
||||
|
||||
void loadFromConfig(json config) {
|
||||
if (config.contains("recorder")) { recorderName = config["recorder"]; }
|
||||
}
|
||||
|
||||
json saveToConfig() {
|
||||
json config;
|
||||
config["recorder"] = recorderName;
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
std::string getName() {
|
||||
return "Start \"" + recorderName + "\"";
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string recorderName;
|
||||
bool valid = false;
|
||||
|
||||
};
|
||||
|
||||
Action StartRecorder() {
|
||||
return Action(new StartRecorderClass);
|
||||
}
|
||||
}
|
53
misc_modules/scheduler/src/actions/tune_vfo.h
Normal file
53
misc_modules/scheduler/src/actions/tune_vfo.h
Normal file
@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include <sched_action.h>
|
||||
#include <utils/freq_formatting.h>
|
||||
|
||||
namespace sched_action {
|
||||
class TuneVFOClass : public ActionClass {
|
||||
public:
|
||||
TuneVFOClass() {}
|
||||
~TuneVFOClass() {}
|
||||
|
||||
void trigger() {
|
||||
|
||||
}
|
||||
|
||||
void prepareEditMenu() {}
|
||||
|
||||
void validateEditMenu() {}
|
||||
|
||||
void showEditMenu() {
|
||||
|
||||
}
|
||||
|
||||
void loadFromConfig(json config) {
|
||||
if (config.contains("vfo")) { vfoName = config["vfo"]; }
|
||||
if (config.contains("frequency")) { frequency = config["frequency"]; }
|
||||
}
|
||||
|
||||
json saveToConfig() {
|
||||
json config;
|
||||
config["vfo"] = vfoName;
|
||||
config["frequency"] = frequency;
|
||||
return config;
|
||||
}
|
||||
|
||||
std::string getName() {
|
||||
return "Tune \"" + vfoName + "\" to " + utils::formatFreq(frequency);
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string vfoName;
|
||||
double frequency;
|
||||
bool valid = false;
|
||||
|
||||
};
|
||||
|
||||
Action TuneVFO() {
|
||||
return Action(new TuneVFOClass);
|
||||
}
|
||||
}
|
148
misc_modules/scheduler/src/main.cpp
Normal file
148
misc_modules/scheduler/src/main.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#include <imgui.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <sched_task.h>
|
||||
#include <map>
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "scheduler",
|
||||
/* Description: */ "SDR++ Scheduler",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
class DemoModule : public ModuleManager::Instance {
|
||||
public:
|
||||
DemoModule(std::string name) {
|
||||
this->name = name;
|
||||
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
||||
|
||||
Task t;
|
||||
t.selected = false;
|
||||
|
||||
json recStartConfig;
|
||||
recStartConfig["recorder"] = "Recorder";
|
||||
|
||||
json tuneVFOConfig;
|
||||
tuneVFOConfig["vfo"] = "Radio";
|
||||
tuneVFOConfig["frequency"] = 103500000.0;
|
||||
|
||||
auto recStart = sched_action::StartRecorder();
|
||||
auto tuneVFO = sched_action::TuneVFO();
|
||||
|
||||
recStart->loadFromConfig(recStartConfig);
|
||||
tuneVFO->loadFromConfig(tuneVFOConfig);
|
||||
|
||||
t.addAction(tuneVFO);
|
||||
t.addAction(recStart);
|
||||
|
||||
tasks["Test"] = t;
|
||||
tasks["Another test"] = t;
|
||||
}
|
||||
|
||||
~DemoModule() {
|
||||
gui::menu.removeEntry(name);
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
static void menuHandler(void* ctx) {
|
||||
DemoModule* _this = (DemoModule*)ctx;
|
||||
|
||||
// If editing, show menu
|
||||
if (!_this->editedTask.empty()) {
|
||||
gui::mainWindow.lockWaterfallControls = true;
|
||||
std::string id = "Edit##scheduler_edit_task_" + _this->name;
|
||||
ImGui::OpenPopup(id.c_str());
|
||||
if (ImGui::BeginPopup(id.c_str(), ImGuiWindowFlags_NoResize)) {
|
||||
bool valid = false;
|
||||
bool open = _this->tasks[_this->editedTask].showEditMenu(_this->editedName, valid);
|
||||
|
||||
// Stop editing of closed
|
||||
if (!open) {
|
||||
// Rename if name changed and valid
|
||||
if (valid && strcmp(_this->editedName, _this->editedTask.c_str())) {
|
||||
Task task = _this->tasks[_this->editedTask];
|
||||
_this->tasks.erase(_this->editedTask);
|
||||
_this->tasks[_this->editedName] = task;
|
||||
}
|
||||
|
||||
// Stop showing edit window
|
||||
_this->editedTask.clear();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable(("freq_manager_bkm_table"+_this->name).c_str(), 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 200))) {
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Countdown");
|
||||
ImGui::TableSetupScrollFreeze(2, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
for (auto& [name, bm] : _this->tasks) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImVec2 min = ImGui::GetCursorPos();
|
||||
|
||||
if (ImGui::Selectable((name + "##_freq_mgr_bkm_name_" + _this->name).c_str(), &bm.selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SelectOnClick)) {
|
||||
// if shift or control isn't pressed, deselect all others
|
||||
if (!ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT) &&
|
||||
!ImGui::IsKeyDown(GLFW_KEY_LEFT_CONTROL) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_CONTROL)) {
|
||||
for (auto& [_name, _bm] : _this->tasks) {
|
||||
if (name == _name) { continue; }
|
||||
_bm.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::TableGetHoveredColumn() >= 0 && ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && _this->editedTask.empty()) {
|
||||
_this->editedTask = name;
|
||||
strcpy(_this->editedName, name.c_str());
|
||||
}
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("todo");
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
std::string editedTask = "";
|
||||
char editedName[1024];
|
||||
|
||||
std::map<std::string, Task> tasks;
|
||||
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new DemoModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
delete (DemoModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// Nothing here
|
||||
}
|
28
misc_modules/scheduler/src/sched_action.h
Normal file
28
misc_modules/scheduler/src/sched_action.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <json.hpp>
|
||||
#include <memory>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace sched_action {
|
||||
class ActionClass {
|
||||
public:
|
||||
virtual ~ActionClass() {
|
||||
spdlog::warn("Base destructor");
|
||||
};
|
||||
virtual void trigger() = 0;
|
||||
virtual void prepareEditMenu() = 0;
|
||||
virtual void validateEditMenu() = 0;
|
||||
virtual void showEditMenu() = 0;
|
||||
virtual void loadFromConfig(json config) = 0;
|
||||
virtual json saveToConfig() = 0;
|
||||
virtual std::string getName() = 0;
|
||||
virtual bool isValid() = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ActionClass> Action;
|
||||
}
|
||||
|
||||
#include <actions/start_recorder.h>
|
||||
#include <actions/tune_vfo.h>
|
75
misc_modules/scheduler/src/sched_task.h
Normal file
75
misc_modules/scheduler/src/sched_task.h
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <imgui.h>
|
||||
#include <gui/style.h>
|
||||
#include <sched_action.h>
|
||||
|
||||
class Task {
|
||||
public:
|
||||
void trigger() {
|
||||
for (auto& act : actions) {
|
||||
act->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void addAction(sched_action::Action act) {
|
||||
actions.push_back(act);
|
||||
}
|
||||
|
||||
bool removeAction(sched_action::Action act) {
|
||||
auto it = std::find(actions.begin(), actions.end(), act);
|
||||
if (it == actions.end()) { return false; }
|
||||
actions.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool showEditMenu(char* name, bool& valid) {
|
||||
ImGui::LeftLabel("Name");
|
||||
ImGui::InputText("##scheduler_task_edit_name", name, 1023);
|
||||
|
||||
if (ImGui::BeginTable("scheduler_task_triggers", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 100))) {
|
||||
ImGui::TableSetupColumn("Triggers");
|
||||
ImGui::TableSetupScrollFreeze(1, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Fill rows here
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("Every day at 00:00:00");
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("scheduler_task_actions", 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, ImVec2(0, 100))) {
|
||||
ImGui::TableSetupColumn("Actions");
|
||||
ImGui::TableSetupScrollFreeze(1, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto& act : actions) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text(act->getName().c_str());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Apply")) {
|
||||
valid = true;
|
||||
return false;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
valid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool selected;
|
||||
|
||||
private:
|
||||
std::vector<sched_action::Action> actions;
|
||||
|
||||
};
|
@ -14,8 +14,8 @@
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "spyserver_source",
|
||||
/* Description: */ "Airspy HF+ source module for SDR++",
|
||||
/* Name: */ "rfspace_source",
|
||||
/* Description: */ "RFspace source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
SDRPP_MOD_INFO {
|
||||
/* Name: */ "spyserver_source",
|
||||
/* Description: */ "Airspy HF+ source module for SDR++",
|
||||
/* Description: */ "SpyServer source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
|
Loading…
Reference in New Issue
Block a user