From c1f621fdf62f9de384833f32716928ad45672095 Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Wed, 1 Sep 2021 21:12:38 +0100 Subject: [PATCH] Start work on local JSON config system. Start using it with Input classes --- .gitignore | 1 + GNUmakefile | 2 +- config.cc | 146 +++++++++++++++++++++++++++++++++++++++++++++ config.h | 53 ++++++++++++++++ config.json.sample | 48 +++++++++++++++ inputlirc.cc | 52 +++++++++++++--- inputlirc.h | 1 + inputman.cc | 141 ++++++++++++++++++++++++++++++++++--------- inputman.h | 2 + main.cc | 34 ++++++++--- objects.mk | 2 +- vepgsettimer.cc | 2 +- 12 files changed, 438 insertions(+), 46 deletions(-) create mode 100644 config.cc create mode 100644 config.h create mode 100644 config.json.sample diff --git a/.gitignore b/.gitignore index 8aa4cc7..26a69ca 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ target-* .project .cproject .#* +config.json diff --git a/GNUmakefile b/GNUmakefile index 36ae7aa..d3a4878 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -94,7 +94,7 @@ LDFLAGS = -Wall $(PICTURES) $(SYSROOT) \ -L=/opt/vc/lib -L=/usr/lib/arm-linux-gnueabihf LIBPATHS = LIBS = -lpthread -lstdc++ -lrt -lbrcmEGL -lbrcmOpenVG -lopenmaxil -lbcm_host -lavformat -lavcodec \ - -lavutil -lswresample -lm -ldl -lfontconfig -lfreetype -lMagick++-6.Q16 -lMagickWand-6.Q16 -lMagickCore-6.Q16 + -lavutil -lswresample -lm -ldl -lfontconfig -lfreetype -lMagick++-6.Q16 -lMagickWand-6.Q16 -lMagickCore-6.Q16 -ljsoncpp OBJECTS = $(OBJ_COMMON) $(OBJ_RASPBERRY) INCLUDES = -isystem=/usr/include/arm-linux-gnueabihf -isystem=/opt/vc/include -isystem=/usr/include/freetype2 -isystem=/usr/include/arm-linux-gnueabihf/ImageMagick-6 -isystem=/usr/include/ImageMagick-6 DEFINES += -DVOMP_PLATFORM_RASPBERRY -DMAGICKCORE_HDRI_ENABLE=0 -DMAGICKCORE_QUANTUM_DEPTH=16 diff --git a/config.cc b/config.cc new file mode 100644 index 0000000..7ebd89b --- /dev/null +++ b/config.cc @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +// http://open-source-parsers.github.io/jsoncpp-docs/doxygen/namespacemembers.html + + +#include "config.h" + +Config* Config::instance = NULL; + +Config::Config() +: jconfigro(jconfig) +{ + instance = this; + applyDefaults(); +} + +Config::~Config() +{ + instance = NULL; +} + +Config* Config::getInstance() +{ + return instance; +} + +void Config::applyDefaults() +{ + // Insert the value only if it doesn't already exist + auto insertBool = [&] (const char* s, const char* k, bool v) { if (jconfig[s][k].isNull()) jconfig[s][k] = v; }; + auto insertString = [&] (const char* s, const char* k, const char* v) { if (jconfig[s][k].isNull()) jconfig[s][k] = v; }; + + insertBool("main", "debug", false); + insertBool("main", "daemonize", true); + + insertBool("log", "enabled", true); + insertString("log", "filename", "stdout"); + insertString("log", "level", "debug"); + + #ifdef VOMP_PLATFORM_RASPBERRY + insertBool("input", "mod_cec_enabled", true); + #else + insertBool("input", "mod_cec_enabled", false); + #endif + + insertBool("input", "mod_udp_enabled", true); + insertBool("input", "mod_lirc_enabled", false); +} + +bool Config::loadFile() +{ + jconfig.clear(); + + std::ifstream configFile("config.json"); + + Json::CharReaderBuilder builder; + builder["collectComments"] = false; + builder["allowTrailingCommas"] = true; + std::string errs; + + bool ok = Json::parseFromStream(builder, configFile, &jconfig, &errs); + + std::cout << errs << std::endl; + + if (!ok) return false; +// std::cout << jconfig << std::endl; +// std::cout << errs << std::endl; + + + applyDefaults(); + + dump(); + + return true; +} + +bool Config::getString(const std::string& section, const std::string& key, std::string& out) const +{ + Json::Value v = jconfigro[section][key]; + if (!v.isString()) return false; + out = v.asString(); + return true; +} + +bool Config::getInt(const std::string& section, const std::string& key, int& out) const +{ + Json::Value v = jconfigro[section][key]; + if (!v.isInt64()) return false; + out = v.asInt64(); + return true; +} + +bool Config::getBool(const std::string& section, const std::string& key, bool& out) const +{ + Json::Value v = jconfigro[section][key]; + if (!v.isBool()) return false; + out = v.asBool(); + return true; +} +/* +bool Config::getBool(const char* section, const char* key, bool& out) const +{ + Json::Value v = jconfigro[section][key]; + if (!v.isBool()) return false; + out = v.asBool(); + return true; +}*/ + +void Config::dump() const +{ + std::cout << jconfigro << std::endl; +} + +bool Config::foreachInArray(const std::string& section, const std::string& key, std::function callback) const +{ + const Json::Value& v = jconfigro[section][key]; + if (!v.isArray()) return false; + + for(Json::Value::const_iterator i = v.begin(); i != v.end(); i++) + { + const Json::Value& jv = *i; + if (jv.isString()) + callback(jv.asString()); + } + + return true; +} + +bool Config::foreachPairInObject(const std::string& section, const std::string& key, std::function callback) const +{ + const Json::Value& v = jconfigro[section][key]; + if (!v.isObject()) return false; + + for(Json::Value::const_iterator i = v.begin(); i != v.end(); i++) + { + const Json::Value& jv = *i; + if (jv.isString()) + callback(i.key().asString(), jv.asString()); + } + + return true; +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..ecd1663 --- /dev/null +++ b/config.h @@ -0,0 +1,53 @@ +/* + Copyright 2021 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP 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 VOMP. If not, see . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + +#include + +#include + +class Config +{ + public: + Config(); + ~Config(); + static Config* getInstance(); + + bool loadFile(); + + bool getString(const std::string& section, const std::string& key, std::string& out) const; + bool getInt(const std::string& section, const std::string& key, int& out) const; + bool getBool(const std::string& section, const std::string& key, bool& out) const; + void dump() const; + bool foreachInArray(const std::string& section, const std::string& key, std::function callback) const; + bool foreachPairInObject(const std::string& section, const std::string& key, std::function callback) const; + + private: + static Config* instance; + + void applyDefaults(); + + Json::Value jconfig; + const Json::Value& jconfigro; + +}; + +#endif diff --git a/config.json.sample b/config.json.sample new file mode 100644 index 0000000..0400f10 --- /dev/null +++ b/config.json.sample @@ -0,0 +1,48 @@ +/* Vomp local config file + To use, rename to config.json and place in the current working directory for vompclient + This file is optional. All fields are optional + Anything in here overrides program defaults + Example data is shown below +*/ + +{ + "main": + { + "debug": false, + "daemonize": true + }, + + "log": + { + "enabled": true, + "filename": "stdout", + "level": "debug" + }, + + "input": + { + "mod_cec_enabled": true, + "mod_udp_enabled": true, + "mod_lirc_enabled": true + }, + + "input_lirc": + { + "lirc_ip": "192.0.2.0", + "lirc_port": 8765, + + "remotes": [ "lirc-remote-name-1", "lirc-remote-name-2" ], + + "lirc-remote-name-1": + { + "KEY_POWER": "POWER", + "KEY_MUTE": "MUTE" + /* etc. List all keys to be used by Vomp + On the left - the Lirc key name. On the right - the Vomp keyname. See input.h (for now) */ + }, + + "lirc-remote-name-2": + { + } + } +} diff --git a/inputlirc.cc b/inputlirc.cc index a1a6c0b..a56e77c 100644 --- a/inputlirc.cc +++ b/inputlirc.cc @@ -21,8 +21,11 @@ #include #include +#include "config.h" #include "log.h" +#include "inputman.h" + #include "inputlirc.h" const char* InputLIRC::myModName = "InputLIRC"; @@ -41,13 +44,48 @@ bool InputLIRC::init() return false; } - /* FIXME - * Hard code remote keys for now. Put lines like this in inputlirc.conf - * The 'THREE' is the VOMP key name, see input.h - * remotes["lirc-remote-name"]["lirc-button-name"] = THREE; - */ - #include "inputlirc.conf" - + bool arraySuccess = Config::getInstance()->foreachInArray("input_lirc", "remotes", [&] (const std::string& remoteName) + { + log->log(myModName, Log::DEBUG, "Remote name: %s", remoteName.c_str()); + + + bool objSuccess = Config::getInstance()->foreachPairInObject("input_lirc", remoteName, [&] (const std::string& lircName, const std::string& vompName) + { + UCHAR keyNumber = InputMan::getVompKeyNumber(vompName.c_str()); + + log->log(myModName, Log::DEBUG, " Lirc: %s, vomp: %s, vomp-number %i", lircName.c_str(), vompName.c_str(), keyNumber); + + remotes[remoteName][lircName] = keyNumber; + }); + + if (!objSuccess) + { + log->log(myModName, Log::ERR, "Error reading config for remote %s", remoteName.c_str()); + } + }); + + if (!arraySuccess) + { + log->log(myModName, Log::DEBUG, "Error reading remotes list"); + } + else + { + log->log(myModName, Log::DEBUG, "Remotes array read OK"); + } + + + using RemotesType = std::map>; + using ButtonsType = std::map; + + for(RemotesType::iterator i = remotes.begin(); i != remotes.end(); i++) + { + ButtonsType& b = i->second; + for(ButtonsType:: iterator j = b.begin(); j != b.end(); j++) + { + log->log(myModName, Log::DEBUG, "%s - %s - %i", i->first.c_str(), j->first.c_str(), j->second); + } + } + return true; } diff --git a/inputlirc.h b/inputlirc.h index e4dee3b..6f38b1b 100644 --- a/inputlirc.h +++ b/inputlirc.h @@ -37,6 +37,7 @@ class InputLIRC : public Input bool init(); void shutdown(); + using Input::start; // Bring all start()s from base class so our start doesn't generate compile warnings bool start(const std::string& ip, USHORT port); void stop(); diff --git a/inputman.cc b/inputman.cc index dc9e2fe..864e558 100644 --- a/inputman.cc +++ b/inputman.cc @@ -17,6 +17,7 @@ along with VOMP. If not, see . */ +#include "config.h" #include "log.h" #include "wremoteconfig.h" #include "wtabbar.h" @@ -64,29 +65,42 @@ bool InputMan::init() else { delete inputLinux; inputLinux = NULL; } - // FIXME enable modules by new config system NCONFIG - -// inputCEC = new InputCEC(); -// ret = inputCEC->init(); -// if (ret) -// oneOK = true; -// else -// { delete inputCEC; inputCEC = NULL; } + bool cecEnabled = true; + Config::getInstance()->getBool("input", "mod_cec_enabled", cecEnabled); + if (cecEnabled) + { + inputCEC = new InputCEC(); + ret = inputCEC->init(); + if (ret) + oneOK = true; + else + { delete inputCEC; inputCEC = NULL; } + } #endif - inputUDP = new InputUDP(); - ret = inputUDP->init(); - if (ret) - oneOK = true; - else - { delete inputUDP; inputUDP = NULL; } + bool udpEnabled = true; + Config::getInstance()->getBool("input", "mod_udp_enabled", udpEnabled); + if (udpEnabled) + { + inputUDP = new InputUDP(); + ret = inputUDP->init(); + if (ret) + oneOK = true; + else + { delete inputUDP; inputUDP = NULL; } + } - inputLirc = new InputLIRC(); - ret = inputLirc->init(); - if (ret) - oneOK = true; - else - { delete inputLirc; inputLirc = NULL; } + bool lircEnabled = false; + Config::getInstance()->getBool("input", "mod_lirc_enabled", lircEnabled); + if (lircEnabled) + { + inputLirc = new InputLIRC(); + ret = inputLirc->init(); + if (ret) + oneOK = true; + else + { delete inputLirc; inputLirc = NULL; } + } #ifdef WIN32 inputWin = new InputWin(); @@ -119,14 +133,31 @@ bool InputMan::start() if (inputUDP && inputUDP->start()) oneOK = true; - - // FIXME - std::string lircIP; - USHORT lircPort = 8765; - // Override lircIP in inputlirc.conf2 -#include "inputlirc.conf2" - if (inputLirc && inputLirc->start(lircIP, lircPort)) oneOK = true; - + if (inputLirc) + { + std::string lircIP; + bool checkA = Config::getInstance()->getString("input", "lirc_ip", lircIP); + int lircPort = 8765; + bool checkB = Config::getInstance()->getInt("input", "lirc_port", lircPort); + + if (!checkA || !checkB) + { + delete inputLirc; + inputLirc = NULL; + } + else + { + if (inputLirc->start(lircIP, lircPort)) + { + oneOK = true; + } + else + { + delete inputLirc; + inputLirc = NULL; + } + } + } if (!oneOK) Log::getInstance()->log("InputMan", Log::CRIT, "InputMan could not start any input module"); @@ -378,6 +409,60 @@ const char* InputMan::getVompKeyName(UCHAR number) } } +const UCHAR InputMan::getVompKeyNumber(const char* vompKeyName) +{ + if (!strcmp(vompKeyName, "VOLUMEUP")) return Input::VOLUMEUP; + else if (!strcmp(vompKeyName, "VOLUMEDOWN")) return Input::VOLUMEDOWN; + else if (!strcmp(vompKeyName, "CHANNELUP")) return Input::CHANNELUP; + else if (!strcmp(vompKeyName, "CHANNELDOWN")) return Input::CHANNELDOWN; + else if (!strcmp(vompKeyName, "ZERO")) return Input::ZERO; + else if (!strcmp(vompKeyName, "ONE")) return Input::ONE; + else if (!strcmp(vompKeyName, "TWO")) return Input::TWO; + else if (!strcmp(vompKeyName, "THREE")) return Input::THREE; + else if (!strcmp(vompKeyName, "FOUR")) return Input::FOUR; + else if (!strcmp(vompKeyName, "FIVE")) return Input::FIVE; + else if (!strcmp(vompKeyName, "SIX")) return Input::SIX; + else if (!strcmp(vompKeyName, "SEVEN")) return Input::SEVEN; + else if (!strcmp(vompKeyName, "EIGHT")) return Input::EIGHT; + else if (!strcmp(vompKeyName, "NINE")) return Input::NINE; + else if (!strcmp(vompKeyName, "POWER")) return Input::POWER; + else if (!strcmp(vompKeyName, "GO")) return Input::GO; + else if (!strcmp(vompKeyName, "BACK")) return Input::BACK; + else if (!strcmp(vompKeyName, "MENU")) return Input::MENU; + else if (!strcmp(vompKeyName, "RED")) return Input::RED; + else if (!strcmp(vompKeyName, "GREEN")) return Input::GREEN; + else if (!strcmp(vompKeyName, "YELLOW")) return Input::YELLOW; + else if (!strcmp(vompKeyName, "BLUE")) return Input::BLUE; + else if (!strcmp(vompKeyName, "MUTE")) return Input::MUTE; + else if (!strcmp(vompKeyName, "RADIO")) return Input::RADIO; + else if (!strcmp(vompKeyName, "REVERSE")) return Input::REVERSE; + else if (!strcmp(vompKeyName, "PLAY")) return Input::PLAY; + else if (!strcmp(vompKeyName, "FORWARD")) return Input::FORWARD; + else if (!strcmp(vompKeyName, "RECORD")) return Input::RECORD; + else if (!strcmp(vompKeyName, "STOP")) return Input::STOP; + else if (!strcmp(vompKeyName, "PAUSE")) return Input::PAUSE; + else if (!strcmp(vompKeyName, "SKIPBACK")) return Input::SKIPBACK; + else if (!strcmp(vompKeyName, "SKIPFORWARD")) return Input::SKIPFORWARD; + else if (!strcmp(vompKeyName, "OK")) return Input::OK; + else if (!strcmp(vompKeyName, "FULL")) return Input::FULL; + else if (!strcmp(vompKeyName, "TV")) return Input::TV; + else if (!strcmp(vompKeyName, "VIDEOS")) return Input::VIDEOS; + else if (!strcmp(vompKeyName, "MUSIC")) return Input::MUSIC; + else if (!strcmp(vompKeyName, "PICTURES")) return Input::PICTURES; + else if (!strcmp(vompKeyName, "GUIDE")) return Input::GUIDE; + else if (!strcmp(vompKeyName, "UP")) return Input::UP; + else if (!strcmp(vompKeyName, "DOWN")) return Input::DOWN; + else if (!strcmp(vompKeyName, "LEFT")) return Input::LEFT; + else if (!strcmp(vompKeyName, "RIGHT")) return Input::RIGHT; + else if (!strcmp(vompKeyName, "PREVCHANNEL")) return Input::PREVCHANNEL; + else if (!strcmp(vompKeyName, "STAR")) return Input::STAR; + else if (!strcmp(vompKeyName, "HASH")) return Input::HASH; + else if (!strcmp(vompKeyName, "PLAYPAUSE")) return Input::PLAYPAUSE; + else if (!strcmp(vompKeyName, "POWERON")) return Input::POWERON; + else if (!strcmp(vompKeyName, "POWEROFF")) return Input::POWEROFF; + else return NULL; +} + std::string InputMan::getHardCodedHardwareKeyNamesForVompKey(UCHAR vompKey) { // Go through each active Input class and get the hardware key name for vompKey diff --git a/inputman.h b/inputman.h index 1705ee2..cc08427 100644 --- a/inputman.h +++ b/inputman.h @@ -117,6 +117,8 @@ class InputMan: public AbstractOption bool saveOptionstoServer(); static const char* getVompKeyName(UCHAR vompKey); + static const UCHAR getVompKeyNumber(const char* vompKeyName); + std::string getHardCodedHardwareKeyNamesForVompKey(UCHAR vompKey); std::string getAllHardwareKeyNamesAssignedToVompKey(UCHAR vompKey); diff --git a/main.cc b/main.cc index 506f7fe..42c9b3a 100644 --- a/main.cc +++ b/main.cc @@ -30,7 +30,6 @@ #include #include - #ifndef WIN32 #include #include @@ -43,6 +42,7 @@ #include #endif +#include "config.h" #include "log.h" #include "util.h" #include "control.h" @@ -59,9 +59,10 @@ void threadSignalReceiverFunction(); #endif -void shutdown(int code); -const std::string& getCommandLineServer(); +[[ noreturn ]] void shutdown(int code); +const std::string& getCommandLineServer(); // NCONFIG +Config* config; Log* logger; Control* control; @@ -80,8 +81,17 @@ int main(int argc, char** argv) bool daemonize = true; bool debugEnabled = false; bool crashed = false; - int c; + + config = new Config(); + if (!config->loadFile()) + { + printf("Parse error in config.json\n"); + shutdown(1); + } + + + int c; while ((c = getopt(argc, argv, "cdns:")) != -1) { switch (c) @@ -125,7 +135,7 @@ int main(int argc, char** argv) // Daemonize -------------------------------------------------------------------------------------------------- - if (daemonize) + if (daemonize) // NCONFIG { // Fork away pid_t forkTest = fork(); @@ -191,6 +201,10 @@ int main(int argc, char** argv) } #endif + // Test area ------------------------------------------------------------------------------------------------------ + + + // Run control ---------------------------------------------------------------------------------------------------- control = new Control(); @@ -212,7 +226,6 @@ int main(int argc, char** argv) control->shutdown(); shutdown(0); - return 0; } // ------------------------------------------------------------------------------------------------------------------- @@ -240,7 +253,7 @@ void threadSignalReceiverFunction() // ------------------------------------------------------------------------------------------------------------------- -void shutdown(int code) +[[ noreturn ]] void shutdown(int code) { // FIXME, send a del all to boxstack first, then get rid of it after control? if (control) // shut down control here in case views have posted messages @@ -262,6 +275,11 @@ void shutdown(int code) delete logger; } + if (config) + { + delete config; + } + exit(code); } @@ -279,7 +297,7 @@ int getClockRealTime(struct timespec *tp) // FIXME - del if all goes chrono return clock_gettime(CLOCK_REALTIME, tp); } -const std::string& getCommandLineServer() +const std::string& getCommandLineServer() // NCONFIG { return argvServer; } diff --git a/objects.mk b/objects.mk index 30d496e..a9d1c05 100644 --- a/objects.mk +++ b/objects.mk @@ -18,7 +18,7 @@ OBJ_COMMON = util.o control.o thread.o timers.o i18n.o udp4.o udp6.o vdpc.o tcp. wprogressbar.o bitmap.o dvbsubtitles.o tfeed.o vteletextview.o \ teletextdecodervbiebu.o teletxt/txtfont.o movieinfo.o seriesinfo.o \ wmovieview.o wseriesview.o tvmedia.o wtvmedia.o wpictureview.o \ - osdvector.o surfacevector.o buffer.o \ + osdvector.o surfacevector.o buffer.o config.o \ playervideorec.o playervideolive.o playerradiolive.o playerradiorec.o OBJ_RASPBERRY = main.o threadp.o osdopenvg.o \ diff --git a/vepgsettimer.cc b/vepgsettimer.cc index 59704d9..71a025a 100644 --- a/vepgsettimer.cc +++ b/vepgsettimer.cc @@ -101,7 +101,7 @@ char* VEpgSetTimer::genTimerString() flags = 1; // hard coded active timer flag - char* startMarginConfig = vdr->configLoad("Timers", "Start margin"); + char* startMarginConfig = vdr->configLoad("Timers", "Start margin"); // NCONFIG if (startMarginConfig) { strncpy(startMargin, startMarginConfig, 9); -- 2.39.2