Add command line interface

pull/2/head
Simon Stürz 2017-12-04 23:03:29 +01:00
parent 83b888360a
commit ef1837f105
40 changed files with 433 additions and 69 deletions

8
config.pri Normal file
View File

@ -0,0 +1,8 @@
QT -= gui
QT += serialport
QMAKE_CXXFLAGS += -Werror -std=c++11
QMAKE_LFLAGS += -std=c++11
sourceDir = $$PWD
buildDir = $$shadowed($$PWD)

View File

@ -1,8 +0,0 @@
#include "core.h"
#include <QDebug>
Core::Core(const int &channel, QObject *parent) :
QObject(parent)
{
m_manager = new ZigbeeNetworkManager(channel, "/dev/ttyS0", this);
}

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
qt-zigbee (0.0.1) xenial; urgency=medium
qtzigbee (0.0.1) xenial; urgency=medium
* Initial release.

52
debian/control vendored
View File

@ -1,4 +1,4 @@
Source: qt-zigbee
Source: qtzigbee
Section: utils
Priority: optional
Maintainer: Simon Stuerz <simon.stuerz@guh.io>
@ -11,18 +11,50 @@ Build-Depends: debhelper (>= 9.0.0),
qt5-qmake,
qtbase5-dev,
qtbase5-dev-tools,
libqt5serialport5-dev
libqt5serialport5-dev,
libncurses5-dev
Package: qt-zigbee
Package: zigbee-cli
Architecture: any
Section: net
Multi-Arch: same
Depends: libqt5network5,
libqt5gui5,
libqt5serialport5,
${shlibs:Depends},
${misc:Depends}
Depends: ${shlibs:Depends},
${misc:Depends},
libqtzigbee1 (= ${binary:Version}),
libncurses5
Description: Zigbee bridge controller Qt5 - tool
Tool written in Qt fro interacting with the NXP Zigbee controller.
Command line tool written in Qt for interacting with the NXP Zigbee controller.
.
This package will install the qt-zigbee tool.
This package will install the zigbee-cli tool.
Package: libqtzigbee1
Section: libs
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
libqt5network5,
libqt5gui5,
libqt5serialport5
Description: Qt 5 based library for Zigbee
Qt 5 based library for Zigbee.
Package: libqtzigbee1-dev
Section: libdevel
Architecture: any
Depends: libqtzigbee1 (= ${binary:Version}),
${shlibs:Depends},
${misc:Depends}
Description: Qt 5 based library for Zigbee - development files
Development files for Qt 5 based Zigbee library.
Package: libqtzigbee1-dbg
Priority: extra
Architecture: any
Section: debug
Depends: libqtzigbee1 (= ${binary:Version}),
${shlibs:Depends},
${misc:Depends}
Description: Qt 5 based library for Zigbee - debug symbols
Debug Symbols for Qt 5 based Zigbee library.

2
debian/libqtzigbee1-dev.install vendored Normal file
View File

@ -0,0 +1,2 @@
usr/lib/libqtzigbee1.so
usr/include/qtzigbee/* usr/include/qtzigbee/

3
debian/libqtzigbee1.install vendored Normal file
View File

@ -0,0 +1,3 @@
usr/lib/libqtzigbee1.so.1
usr/lib/libqtzigbee1.so.1.0
usr/lib/libqtzigbee1.so.1.0.0

3
debian/rules vendored
View File

@ -16,6 +16,9 @@ include /usr/share/dpkg/buildflags.mk
%:
dh $@ --buildsystem=qmake --parallel
override_dh_strip:
dh_strip --dbg-package=libqtzigbee1-dbg
override_dh_auto_build:
make -j$(DEB_PARALLEL_JOBS)

1
debian/zigbee-cli.install vendored Normal file
View File

@ -0,0 +1 @@
usr/bin/zigbee-cli

View File

@ -0,0 +1,42 @@
include(../config.pri)
TARGET = qtzigbee1
TEMPLATE = lib
target.path = /usr/lib
SOURCES += \
interface/zigbeeinterface.cpp \
interface/zigbeeinterfacemessage.cpp \
interface/zigbeeinterfacerequest.cpp \
interface/zigbeeinterfacereply.cpp \
zigbeenetworkmanager.cpp \
zigbee.cpp \
loggingcategory.cpp \
zigbeebridgecontroller.cpp \
zigbeeutils.cpp \
zigbeenode.cpp \
zigbeeaddress.cpp \
HEADERS += \
interface/zigbeeinterface.h \
interface/zigbeeinterfacemessage.h \
interface/zigbeeinterfacerequest.h \
interface/zigbeeinterfacereply.h \
zigbeenetworkmanager.h \
zigbee.h \
loggingcategory.h \
zigbeebridgecontroller.h \
zigbeeutils.h \
zigbeenode.h \
zigbeeaddress.h \
INSTALLS += target
# install header file with relative subdirectory
for(header, HEADERS) {
path = /usr/include/qtzigbee/$${dirname(header)}
eval(headers_$${path}.files += $${header})
eval(headers_$${path}.path = $${path})
eval(INSTALLS *= headers_$${path})
}

View File

@ -15,7 +15,7 @@ ZigbeeNetworkManager::ZigbeeNetworkManager(const int &channel, const QString &se
qCDebug(dcZigbee()) << "Bridge controller started successfully on" << serialPort;
} else {
qCCritical(dcZigbee()) << "The zigbee controller is not available on" << serialPort;
//return;
return;
}
QSettings settings;
@ -55,6 +55,8 @@ ZigbeeNetworkManager::ZigbeeNetworkManager(const int &channel, const QString &se
permitJoining();
//getPermitJoiningStatus();
//startScan();
initiateTouchLink();
}
QString ZigbeeNetworkManager::controllerVersion() const
@ -249,6 +251,15 @@ void ZigbeeNetworkManager::enableWhitelist()
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onEnableWhitelistFinished);
}
void ZigbeeNetworkManager::initiateTouchLink()
{
ZigbeeInterfaceRequest request(ZigbeeInterfaceMessage(Zigbee::MessageTypeInitiateTouchlink));
request.setDescription("Initiate touch link");
ZigbeeInterfaceReply *reply = controller()->sendRequest(request);
connect(reply, &ZigbeeInterfaceReply::finished, this, &ZigbeeNetworkManager::onInitiateTouchLinkFinished);
}
void ZigbeeNetworkManager::requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile)
{
QByteArray data;
@ -508,6 +519,19 @@ void ZigbeeNetworkManager::onEnableWhitelistFinished()
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::onInitiateTouchLinkFinished()
{
ZigbeeInterfaceReply *reply = static_cast<ZigbeeInterfaceReply *>(sender());
reply->deleteLater();
if (reply->status() != ZigbeeInterfaceReply::Success) {
qCWarning(dcZigbeeController()) << "Could not" << reply->request().description() << reply->status() << reply->statusErrorMessage();
return;
}
qCDebug(dcZigbeeController()) << reply->request().description() << "finished successfully";
}
void ZigbeeNetworkManager::processLoggingMessage(const ZigbeeInterfaceMessage &message)
{
quint8 logLevel = static_cast<quint8>(message.data().at(0));
@ -746,7 +770,6 @@ void ZigbeeNetworkManager::processAttributeReport(const ZigbeeInterfaceMessage &
void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage &message)
{
quint16 shortAddress = message.data().at(0);
shortAddress <<= 8;
shortAddress |= message.data().at(1);
@ -755,7 +778,7 @@ void ZigbeeNetworkManager::processLeaveIndication(const ZigbeeInterfaceMessage &
qCDebug(dcZigbee()) << "Node leaving:" << ZigbeeUtils::convertUint16ToHexString(shortAddress) << rejoining;
// remove node
// TODO: remove node
}
void ZigbeeNetworkManager::onMessageReceived(const ZigbeeInterfaceMessage &message)

View File

@ -43,6 +43,8 @@ private:
void getPermitJoiningStatus();
void enableWhitelist();
void initiateTouchLink();
void requestMatchDescriptor(const quint16 &shortAddress, const Zigbee::ZigbeeProfile &profile);
signals:
@ -66,6 +68,8 @@ private slots:
void onPermitJoiningFinished();
void onEnableWhitelistFinished();
void onInitiateTouchLinkFinished();
void onRequestMatchDescriptorFinished();

View File

@ -1,38 +1,5 @@
QT -= gui
QT += serialport
TEMPLATE = subdirs
CONFIG += ordered
CONFIG += c++11 console
CONFIG -= app_bundle
SUBDIRS += libqtzigbee zigbee-cli
TARGET = zigbee-cli
target.path = /usr/bin
INSTALLS += target
SOURCES += main.cpp \
core.cpp \
interface/zigbeeinterface.cpp \
interface/zigbeeinterfacemessage.cpp \
interface/zigbeeinterfacerequest.cpp \
interface/zigbeeinterfacereply.cpp \
zigbeenetworkmanager.cpp \
zigbee.cpp \
loggingcategory.cpp \
zigbeebridgecontroller.cpp \
zigbeeutils.cpp \
zigbeenode.cpp \
zigbeeaddress.cpp
HEADERS += \
core.h \
interface/zigbeeinterface.h \
interface/zigbeeinterfacemessage.h \
interface/zigbeeinterfacerequest.h \
interface/zigbeeinterfacereply.h \
zigbeenetworkmanager.h \
zigbee.h \
loggingcategory.h \
zigbeebridgecontroller.h \
zigbeeutils.h \
zigbeenode.h \
zigbeeaddress.h

35
zigbee-cli/core.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "core.h"
#include "terminalcommander.h"
#include "loggingcategory.h"
Core::Core(const int &channel, QObject *parent) :
QObject(parent)
{
m_manager = new ZigbeeNetworkManager(channel, "/dev/ttyS0", this);
TerminalCommander::instance();
// Set commands
TerminalCommander::instance()->start();
m_testTimer = new QTimer(this);
m_testTimer->setInterval(5000);
m_testTimer->setSingleShot(false);
connect(m_testTimer, &QTimer::timeout, this, &Core::onTimeout);
m_testTimer->start();
connect(TerminalCommander::instance(), &TerminalCommander::commandReceived, this, &Core::onCommandReceived);
}
void Core::onTimeout()
{
qCDebug(dcZigbee()) << "[+] This is a test message from the zigbee thread";
}
void Core::onCommandReceived(const QStringList &tokens)
{
qCDebug(dcZigbee()) << "Command received:" << tokens;
}

View File

@ -2,6 +2,11 @@
#define CORE_H
#include <QObject>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <readline/readline.h>
#include "zigbeenetworkmanager.h"
@ -13,11 +18,13 @@ public:
private:
ZigbeeNetworkManager *m_manager;
QTimer *m_testTimer;
signals:
public slots:
private slots:
void onTimeout();
void onCommandReceived(const QStringList &tokens);
};
#endif // CORE_H

View File

@ -1,10 +1,14 @@
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QDateTime>
#include <unistd.h>
#include <stdio.h>
#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";
@ -33,25 +37,23 @@ static void consoleLogHandler(QtMsgType type, const QMessageLogContext& context,
{
switch (type) {
case QtInfoMsg:
fprintf(stdout, " I | %s: %s\n", context.category, message.toUtf8().data());
TerminalCommander::instance()->printToTerminal(QString("%1: %2\n").arg(context.category).arg(message.toUtf8().data()));
break;
case QtDebugMsg:
fprintf(stdout, " I | %s: %s\n", context.category, message.toUtf8().data());
TerminalCommander::instance()->printToTerminal(QString("%1: %2\n").arg(context.category).arg(message.toUtf8().data()));
break;
case QtWarningMsg:
fprintf(stdout, "%s W | %s: %s%s\n", warning, context.category, message.toUtf8().data(), normal);
TerminalCommander::instance()->printToTerminal(QString("%1%2: %3%4\n").arg(terminalColorYellow).arg(context.category).arg(message.toUtf8().data()).arg(terminalColorNormal));
break;
case QtCriticalMsg:
fprintf(stdout, "%s C | %s: %s%s\n", error, context.category, message.toUtf8().data(), normal);
TerminalCommander::instance()->printToTerminal(QString("%1%2: %3%4\n").arg(terminalColorRed).arg(context.category).arg(message.toUtf8().data()).arg(terminalColorNormal));
break;
case QtFatalMsg:
fprintf(stdout, "%s F | %s: %s%s\n", error, context.category, message.toUtf8().data(), normal);
TerminalCommander::instance()->printToTerminal(QString("%1%2: %3%4\n").arg(terminalColorRed).arg(context.category).arg(message.toUtf8().data()).arg(terminalColorNormal));
break;
}
fflush(stdout);
}
int main(int argc, char *argv[])
{
qInstallMessageHandler(consoleLogHandler);
@ -105,8 +107,6 @@ int main(int argc, char *argv[])
}
}
Core core(channel);
return application.exec();

View File

@ -0,0 +1,6 @@
#include "terminalcommand.h"
TerminalCommand::TerminalCommand(QObject *parent) : QObject(parent)
{
}

View File

@ -0,0 +1,17 @@
#ifndef TERMINALCOMMAND_H
#define TERMINALCOMMAND_H
#include <QObject>
class TerminalCommand : public QObject
{
Q_OBJECT
public:
explicit TerminalCommand(QObject *parent = nullptr);
signals:
public slots:
};
#endif // TERMINALCOMMAND_H

View File

@ -0,0 +1,145 @@
#include "terminalcommander.h"
#include "loggingcategory.h"
#include <QStringList>
static char **commandCompletion(const char* text, int start, int end);
static char *commandCompletionGenerator(const char *text, int state);
static TerminalCommander *s_instance = nullptr;
TerminalCommander *TerminalCommander::instance()
{
if (!s_instance)
s_instance = new TerminalCommander();
return s_instance;
}
QStringList TerminalCommander::commands() const
{
return m_commands;
}
TerminalCommander::TerminalCommander(QObject *parent) :
QThread(parent)
{
m_commands.append("start");
m_commands.append("stop");
m_commands.append("explode");
}
TerminalCommander::~TerminalCommander()
{
mutex.lock();
m_abort = true;
mutex.unlock();
wait();
}
void TerminalCommander::printToTerminal(const QString &message)
{
rl_printf("%s", message.toStdString().data());
}
void TerminalCommander::stopProcess()
{
mutex.lock();
m_abort = true;
mutex.unlock();
}
void TerminalCommander::run()
{
m_abort = false;
rl_attempted_completion_function = commandCompletion;
rl_set_prompt(QString("%1[zigbee]%2 ").arg(terminalColorBoldGray).arg(terminalColorNormal).toStdString().data());
rl_redisplay();
rl_bind_key('\t',rl_complete);
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);
}
}
}
if (m_abort)
return;
msleep(10);
}
void TerminalCommander::rl_printf(const char *fmt, ...)
{
va_list args;
bool save_input;
char *saved_line;
int saved_point;
save_input = !RL_ISSTATE(RL_STATE_DONE);
if (save_input) {
saved_point = rl_point;
saved_line = rl_copy_text(0, rl_end);
rl_save_prompt();
rl_replace_line("", 0);
rl_redisplay();
}
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
if (save_input) {
rl_restore_prompt();
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);
}
}
char **commandCompletion(const char *text, int start, int end)
{
Q_UNUSED(end)
rl_completion_append_character = '\0';
char **matches = (char **) NULL;
if (start == 0) {
matches = rl_completion_matches ((char *) text, &commandCompletionGenerator);
}
return matches;
}
char *commandCompletionGenerator(const char *text, int state)
{
//qCDebug(dcZigbee) << text << state << TerminalCommander::instance()->commands();
static int list_index, len;
const char *name;
if (!state) {
list_index = 0;
len = strlen(text);
}
while (list_index < TerminalCommander::instance()->commands().count() && (name = TerminalCommander::instance()->commands().at(list_index).toStdString().data())) {
list_index++;
if (strncmp (name, text, len) == 0) return strdup (name);
}
// If no names matched, then return NULL.
return ((char *) NULL);
}

View File

@ -0,0 +1,54 @@
#ifndef TERMINALCOMMANDER_H
#define TERMINALCOMMANDER_H
#include <QObject>
#include <QThread>
#include <QMutex>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
static const char *const terminalColorNormal = "\033[0m";
static const char *const terminalColorYellow = "\x1B[0;93m";
static const char *const terminalColorRed = "\x1B[0;91m";
static const char *const terminalColorBlue = "\x1B[0;94m";
static const char *const terminalColorGreen = "\e[31m";
static const char *const terminalColorBoldGray = "\x1B[1;30m";
static const char *const terminalColorBoldNormal = "\x1B[1;37m";
class TerminalCommander : public QThread
{
Q_OBJECT
public:
static TerminalCommander* instance();
QStringList commands() const;
void printToTerminal(const QString &message);
private:
explicit TerminalCommander(QObject *parent = nullptr);
~TerminalCommander();
bool m_abort = false;
QMutex mutex;
QStringList m_commands;
static void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
signals:
void commandReceived(const QStringList &tokens);
protected:
void run() override;
public slots:
void stopProcess();
};
#endif // TERMINALCOMMANDER_H

23
zigbee-cli/zigbee-cli.pro Normal file
View File

@ -0,0 +1,23 @@
include(../config.pri)
CONFIG += console
CONFIG -= app_bundle
TARGET = zigbee-cli
LIBS += -lncurses -L$$buildDir/libqtzigbee -lqtzigbee1 -lreadline
INCLUDEPATH += ../libqtzigbee/
target.path = /usr/bin
INSTALLS += target
SOURCES += main.cpp \
core.cpp \
terminalcommander.cpp \
terminalcommand.cpp
HEADERS += \
core.h \
terminalcommander.h \
terminalcommand.h