diff --git a/debian/control b/debian/control index 7497e40..7b2f05f 100644 --- a/debian/control +++ b/debian/control @@ -12,7 +12,7 @@ Build-Depends: debhelper (>= 9.0.0), qtbase5-dev, qtbase5-dev-tools, libqt5serialport5-dev, - libncurses5-dev + libreadline-dev Package: zigbee-cli Architecture: any diff --git a/libqtzigbee/zigbeenetworkmanager.cpp b/libqtzigbee/zigbeenetworkmanager.cpp index 61ff6bd..704b5d4 100644 --- a/libqtzigbee/zigbeenetworkmanager.cpp +++ b/libqtzigbee/zigbeenetworkmanager.cpp @@ -69,6 +69,11 @@ QList ZigbeeNetworkManager::nodeList() const return m_nodeList; } +bool ZigbeeNetworkManager::networkRunning() const +{ + return m_networkRunning; +} + void ZigbeeNetworkManager::reset() { qCCritical(dcZigbee()) << "Reset networkmanager: TODO: needs to be implementet"; diff --git a/libqtzigbee/zigbeenetworkmanager.h b/libqtzigbee/zigbeenetworkmanager.h index 2d1bcff..8e8cf8b 100644 --- a/libqtzigbee/zigbeenetworkmanager.h +++ b/libqtzigbee/zigbeenetworkmanager.h @@ -19,13 +19,18 @@ public: void reset(); + + bool networkRunning() const; + private: ZigbeeBridgeController *m_controller = nullptr; QString m_controllerVersion; - quint64 m_extendedPanId; + quint64 m_extendedPanId = 0; QList m_nodeList; + bool m_networkRunning = false; + quint64 generateRandomPanId(); // Controller methods @@ -48,6 +53,7 @@ private: void requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile); signals: + void runningChanged(const bool &running); private slots: void onMessageReceived(const ZigbeeInterfaceMessage &message); @@ -72,7 +78,6 @@ private slots: void onRequestMatchDescriptorFinished(); - // Process controller notifications/messages void processLoggingMessage(const ZigbeeInterfaceMessage &message); void processFactoryNewRestart(const ZigbeeInterfaceMessage &message); diff --git a/zigbee-cli/core.cpp b/zigbee-cli/core.cpp index 5fe01ef..e4b04e0 100644 --- a/zigbee-cli/core.cpp +++ b/zigbee-cli/core.cpp @@ -1,15 +1,22 @@ #include "core.h" -#include "terminalcommander.h" #include "loggingcategory.h" +#include "QCoreApplication" Core::Core(const int &channel, QObject *parent) : QObject(parent) { m_manager = new ZigbeeNetworkManager(channel, "/dev/ttyS0", this); - TerminalCommander::instance(); - // Set commands + m_commands.append(TerminalCommand("run", "Run the zigbee controller in a normal non interactive mode.")); + m_commands.append(TerminalCommand("start", "Start the zigbee network")); + m_commands.append(TerminalCommand("stop", "Stop the zigbee network")); + m_commands.append(TerminalCommand("reset", "Reset the zigbee controller")); + m_commands.append(TerminalCommand("scan", "Start scanning for zigbee networks")); + m_commands.append(TerminalCommand("version", "Print the version of the zigbee controll bridge firmware")); + m_commands.append(TerminalCommand("permit-join", "Permit nodes to join the network")); + + TerminalCommander::instance()->setCommands(m_commands); TerminalCommander::instance()->start(); @@ -18,9 +25,10 @@ Core::Core(const int &channel, QObject *parent) : m_testTimer->setSingleShot(false); connect(m_testTimer, &QTimer::timeout, this, &Core::onTimeout); - m_testTimer->start(); + //m_testTimer->start(); connect(TerminalCommander::instance(), &TerminalCommander::commandReceived, this, &Core::onCommandReceived); + connect(TerminalCommander::instance(), &TerminalCommander::finished, QCoreApplication::instance(), &QCoreApplication::quit); } void Core::onTimeout() @@ -30,6 +38,26 @@ void Core::onTimeout() void Core::onCommandReceived(const QStringList &tokens) { - qCDebug(dcZigbee()) << "Command received:" << tokens; + TerminalCommand command; + foreach (const TerminalCommand &terminalCommand, m_commands) { + if (tokens.first() == terminalCommand.command()) { + command = terminalCommand; + break; + } + } + + if (!command.isValid()) { + qCWarning(dcZigbee()) << "Unknown command" << tokens; + return; + } + + // TODO: Process command + qCDebug(dcZigbee()) << "Executing" << tokens.join(" "); + + if (command.command() == "start") { + m_manager->startNetwork(); + } else if (command.command() == "version") { + m_manager->getVersion(); + } } diff --git a/zigbee-cli/core.h b/zigbee-cli/core.h index af1bc64..b5adee2 100644 --- a/zigbee-cli/core.h +++ b/zigbee-cli/core.h @@ -8,6 +8,7 @@ #include #include +#include "terminalcommander.h" #include "zigbeenetworkmanager.h" class Core : public QObject @@ -17,8 +18,10 @@ public: explicit Core(const int &channel, QObject *parent = nullptr); private: - ZigbeeNetworkManager *m_manager; - QTimer *m_testTimer; + ZigbeeNetworkManager *m_manager = nullptr; + QTimer *m_testTimer = nullptr; + + QList m_commands; signals: diff --git a/zigbee-cli/main.cpp b/zigbee-cli/main.cpp index de0b089..80fe4af 100644 --- a/zigbee-cli/main.cpp +++ b/zigbee-cli/main.cpp @@ -4,18 +4,60 @@ #include #include #include +#include +#include + #include "core.h" -#include "zigbeecommander.h" #include "loggingcategory.h" #include "terminalcommander.h" -static const char *const normal = "\033[0m"; -static const char *const warning = "\e[33m"; -static const char *const error = "\e[31m"; - static QHash s_loggingFilters; +//static bool s_aboutToShutdown = false; + +//static void catchUnixSignals(const std::vector& quitSignals, const std::vector& ignoreSignals = std::vector()) { +// auto handler = [](int sig) -> void { +// switch (sig) { +// case SIGQUIT: +// qCDebug(dcZigbee()) << "Cought SIGQUIT quit signal..."; +// break; +// case SIGINT: +// qCDebug(dcZigbee()) << "Cought SIGINT quit signal..."; +// break; +// case SIGTERM: +// qCDebug(dcZigbee()) << "Cought SIGTERM quit signal..."; +// break; +// case SIGHUP: +// qCDebug(dcZigbee()) << "Cought SIGHUP quit signal..."; +// break; +// case SIGSEGV: { +// qCDebug(dcZigbee()) << "Cought SIGSEGV signal. Segmentation fault!"; +// exit(1); +// } +// default: +// break; +// } + +// if (s_aboutToShutdown) { +// return; +// } + +// s_aboutToShutdown = true; +// TerminalCommander::instance()->destroy(); +// TerminalCommander::instance()->quit(); +// }; + + + +// // all these signals will be ignored. +// for (int sig : ignoreSignals) +// signal(sig, SIG_IGN); + +// for (int sig : quitSignals) +// signal(sig, handler); +//} + static void loggingCategoryFilter(QLoggingCategory *category) { // If this is a known category @@ -59,6 +101,9 @@ int main(int argc, char *argv[]) qInstallMessageHandler(consoleLogHandler); QCoreApplication application(argc, argv); + + //catchUnixSignals({SIGQUIT, SIGINT, SIGTERM, SIGHUP, SIGSEGV}); + application.setOrganizationName("guh"); application.setApplicationName("qt-zigbee"); diff --git a/zigbee-cli/terminalcommand.cpp b/zigbee-cli/terminalcommand.cpp index e9de729..852c3c8 100644 --- a/zigbee-cli/terminalcommand.cpp +++ b/zigbee-cli/terminalcommand.cpp @@ -1,6 +1,58 @@ #include "terminalcommand.h" -TerminalCommand::TerminalCommand(QObject *parent) : QObject(parent) +TerminalCommand::TerminalCommand() { } + +TerminalCommand::TerminalCommand(const QString &command, const QString &description) : + m_command(command), + m_description(description) +{ + +} + +QString TerminalCommand::command() const +{ + return m_command; +} + +void TerminalCommand::setCommand(const QString &command) +{ + m_command = command; +} + +QString TerminalCommand::description() const +{ + return m_description; +} + +void TerminalCommand::setDescription(const QString &description) +{ + m_description = description; +} + +QStringList TerminalCommand::mandatoryParameters() const +{ + return m_mandatorysParameters; +} + +void TerminalCommand::setMandatoryParameters(const QStringList ¶meters) +{ + m_mandatorysParameters = parameters; +} + +QStringList TerminalCommand::optionalParameters() const +{ + return m_optionalParameters; +} + +void TerminalCommand::setOptionalParameters(const QStringList ¶meters) +{ + m_optionalParameters = parameters; +} + +bool TerminalCommand::isValid() +{ + return !m_command.isEmpty() && !m_description.isEmpty(); +} diff --git a/zigbee-cli/terminalcommand.h b/zigbee-cli/terminalcommand.h index d083c82..bf712eb 100644 --- a/zigbee-cli/terminalcommand.h +++ b/zigbee-cli/terminalcommand.h @@ -1,17 +1,35 @@ #ifndef TERMINALCOMMAND_H #define TERMINALCOMMAND_H -#include +#include +#include -class TerminalCommand : public QObject +class TerminalCommand { - Q_OBJECT public: - explicit TerminalCommand(QObject *parent = nullptr); + explicit TerminalCommand(); + explicit TerminalCommand(const QString &command, const QString &description); -signals: + QString command() const; + void setCommand(const QString &command); + + QString description() const; + void setDescription(const QString &description); + + QStringList mandatoryParameters() const; + void setMandatoryParameters(const QStringList ¶meters); + + QStringList optionalParameters() const; + void setOptionalParameters(const QStringList ¶meters); + + bool isValid(); + +private: + QString m_command; + QString m_description; + QStringList m_mandatorysParameters; + QStringList m_optionalParameters; -public slots: }; -#endif // TERMINALCOMMAND_H \ No newline at end of file +#endif // TERMINALCOMMAND_H diff --git a/zigbee-cli/terminalcommander.cpp b/zigbee-cli/terminalcommander.cpp index c436b19..07a057a 100644 --- a/zigbee-cli/terminalcommander.cpp +++ b/zigbee-cli/terminalcommander.cpp @@ -16,25 +16,34 @@ TerminalCommander *TerminalCommander::instance() return s_instance; } -QStringList TerminalCommander::commands() const +void TerminalCommander::destroy() +{ + qCDebug(dcZigbee()) << "Shut down terminal commander. Have a nice day.b"; + stopProcess(); +} + +QList TerminalCommander::commands() const { return m_commands; } +void TerminalCommander::setCommands(QList commands) +{ + m_commands.append(commands); +} + TerminalCommander::TerminalCommander(QObject *parent) : QThread(parent) { - m_commands.append("start"); - m_commands.append("stop"); - m_commands.append("explode"); + // Native terminal commands + m_commands.append(TerminalCommand("exit", "Exit this application")); + m_commands.append(TerminalCommand("quit", "Quit this application")); + m_commands.append(TerminalCommand("help", "Print this help message")); } TerminalCommander::~TerminalCommander() { - mutex.lock(); - m_abort = true; - mutex.unlock(); - + destroy(); wait(); } @@ -45,9 +54,8 @@ void TerminalCommander::printToTerminal(const QString &message) void TerminalCommander::stopProcess() { - mutex.lock(); + QMutexLocker locker(&m_mutex); m_abort = true; - mutex.unlock(); } void TerminalCommander::run() @@ -57,24 +65,51 @@ void TerminalCommander::run() rl_set_prompt(QString("%1[zigbee]%2 ").arg(terminalColorBoldGray).arg(terminalColorNormal).toStdString().data()); rl_redisplay(); - rl_bind_key('\t',rl_complete); + rl_clear_signals(); + rl_catch_signals = 1; + while (true) { char *buffer = readline(QString("%1[zigbee]%2 ").arg(terminalColorBoldGray).arg(terminalColorNormal).toStdString().data()); rl_bind_key('\t',rl_complete); if (buffer) { if (!QString(buffer).isEmpty()) { QStringList tokens = QString(buffer).split(" "); - emit commandReceived(tokens); - add_history(buffer); - free(buffer); + + // Check quit + if (tokens.count() == 1 && (tokens.first() == "quit" || tokens.first() == "exit")) { + qDebug() << ""; + rl_on_new_line(); + rl_replace_line("", 0); + rl_redisplay(); + qCDebug(dcZigbee()) << "Terminal thread stopped"; + free(buffer); + stopProcess(); + return; + + } else if (tokens.count() == 1 && (tokens.first() == "?" || tokens.first() == "help")) { + printHelpMessage(); + add_history(buffer); + free(buffer); + } else { + emit commandReceived(tokens); + add_history(buffer); + free(buffer); + } } } - } - if (m_abort) - return; + QMutexLocker locker(&m_mutex); + if (m_abort) { + qDebug() << ""; + rl_on_new_line(); + rl_replace_line("", 0); + rl_redisplay(); + qCDebug(dcZigbee()) << "Terminal thread stopped"; + return; + } + } msleep(10); } @@ -107,7 +142,21 @@ void TerminalCommander::rl_printf(const char *fmt, ...) rl_redisplay(); free(saved_line); } +} +void TerminalCommander::signalHandler(int status) +{ + Q_UNUSED(status) + qCDebug(dcZigbee()) << "Terminal thread stopped"; + TerminalCommander::instance()->stopProcess(); +} + +void TerminalCommander::printHelpMessage() +{ + for (int i = 0; i < m_commands.count(); i++) { + QString command = QString("\t%1 %2").arg(m_commands.at(i).command(), -20).arg(m_commands.at(i).description()); + rl_printf("%s\n",command.toStdString().data()); + } } char **commandCompletion(const char *text, int start, int end) @@ -135,7 +184,7 @@ char *commandCompletionGenerator(const char *text, int state) len = strlen(text); } - while (list_index < TerminalCommander::instance()->commands().count() && (name = TerminalCommander::instance()->commands().at(list_index).toStdString().data())) { + while (list_index < TerminalCommander::instance()->commands().count() && (name = TerminalCommander::instance()->commands().at(list_index).command().toStdString().data())) { list_index++; if (strncmp (name, text, len) == 0) return strdup (name); } diff --git a/zigbee-cli/terminalcommander.h b/zigbee-cli/terminalcommander.h index 245460a..92ab24a 100644 --- a/zigbee-cli/terminalcommander.h +++ b/zigbee-cli/terminalcommander.h @@ -11,6 +11,8 @@ #include #include +#include "terminalcommand.h" + static const char *const terminalColorNormal = "\033[0m"; static const char *const terminalColorYellow = "\x1B[0;93m"; static const char *const terminalColorRed = "\x1B[0;91m"; @@ -24,21 +26,26 @@ class TerminalCommander : public QThread Q_OBJECT public: static TerminalCommander* instance(); + void destroy(); + + QList commands() const; + void setCommands(QList commands); - QStringList commands() const; void printToTerminal(const QString &message); private: explicit TerminalCommander(QObject *parent = nullptr); ~TerminalCommander(); - bool m_abort = false; - QMutex mutex; + QList m_commands; - QStringList m_commands; + QMutex m_mutex; + bool m_abort = false; static void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + static void signalHandler(int status); + void printHelpMessage(); signals: void commandReceived(const QStringList &tokens);