diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bd9c81..13b0441 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 2.8) project(s20) add_definitions(-std=gnu++11) +set(CMAKE_AUTOMOC TRUE) find_package(Qt5 REQUIRED Core Network) -add_executable(s20 main.cpp socket.cpp consolereader.cpp) +add_executable(s20 main.cpp socket.cpp consolereader.cpp server.cpp) target_link_libraries(s20 Qt5::Core Qt5::Network) diff --git a/consolereader.cpp b/consolereader.cpp index 851775a..064300d 100644 --- a/consolereader.cpp +++ b/consolereader.cpp @@ -19,14 +19,10 @@ #include "consolereader.h" -ConsoleReader::ConsoleReader(std::vector *sockets_vector) +ConsoleReader::ConsoleReader ( std::vector *sockets_vector ) { sockets = sockets_vector; -} - -ConsoleReader::~ConsoleReader() -{ - // + start(); } void ConsoleReader::run() @@ -35,46 +31,46 @@ void ConsoleReader::run() unsigned int number = 0; bool cont = true; - while (cont) + while ( cont ) { - listSockets(); - std::cout << "d - update table data\nn - change socket name\ns - pick another socket (default is 1)\np - toggle power state\nq - quit" << std::endl; std::cin >> command; - switch (command) + switch ( command ) { - case 'd': - (*sockets)[number].tableData(); - break; - case 'n': + case 'd': + ( *sockets ) [number]->tableData(); + break; + case 'n': // std::cout << "Enter new name (max 16 characters)" << std::endl; // string name; // std::cin >> name; - (*sockets)[number].changeSocketName(); - break; - case 'p': - (*sockets)[number].toggle(); - break; - case 'q': - cont = false; - emit(QCoreApplication::quit()); - break; - case 's': - std::cin >> number; - --number; // count from 0 - break; - default: - std::cout << "Invalid command" << std::endl; + ( *sockets ) [number]->changeSocketName(); + break; + case 'p': + ( *sockets ) [number]->toggle(); + break; + case 'q': + cont = false; + emit ( QCoreApplication::quit() ); + break; + case 's': + std::cin >> number; + --number; // count from 0 + listSockets(); + break; + default: + std::cout << "Invalid command: try again" << std::endl; } } } void ConsoleReader::listSockets() { - for (std::vector::const_iterator i = sockets->begin() ; i != sockets->end(); ++i) + for ( std::vector::const_iterator i = sockets->begin() ; i != sockets->end(); ++i ) { std::cout << "___________________________________________________________________________\n" << std::endl; - std::cout << "IP Address: " << i->ip.toString().toStdString() << "\t MAC Address: " << i->mac.toHex().toStdString() << "\t Power: " << (i->powered ? "On" : "Off") << std::endl; - std::cout << "Socket Name: " << i->name.toStdString() << "\t Remote Password: " << i->remotePassword.toStdString() << std::endl; + std::cout << "IP Address: " << (*i)->ip.toString().toStdString() << "\t MAC Address: " << (*i)->mac.toHex().toStdString() << "\t Power: " << ( (*i)->powered ? "On" : "Off" ) << std::endl; + std::cout << "Socket Name: " << (*i)->name.toStdString() << "\t Remote Password: " << (*i)->remotePassword.toStdString() << std::endl; } std::cout << "___________________________________________________________________________\n" << std::endl; + std::cout << "d - update table data\nn - change socket name\ns - pick another socket (default is 1)\np - toggle power state\nq - quit" << std::endl; } diff --git a/consolereader.h b/consolereader.h index 7f34e9b..886949c 100644 --- a/consolereader.h +++ b/consolereader.h @@ -26,13 +26,12 @@ class ConsoleReader : public QThread { public: - ConsoleReader(std::vector *sockets_vector); - ~ConsoleReader(); + ConsoleReader ( std::vector *sockets_vector ); void run (); + void listSockets(); private: - void listSockets(); - std::vector *sockets; + std::vector *sockets; }; #endif /* CONSOLEREADER_H */ diff --git a/main.cpp b/main.cpp index c552021..00feffb 100644 --- a/main.cpp +++ b/main.cpp @@ -19,32 +19,22 @@ #include #include "consolereader.h" -#include "discover.h" - -void listSockets(std::vector const &sockets); +#include "server.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - QUdpSocket *udpSocketSend = new QUdpSocket(); - QUdpSocket *udpSocketGet = new QUdpSocket(); + std::vector *sockets = new std::vector; + Server server(sockets); + ConsoleReader *reader = new ConsoleReader(sockets); + QThread::sleep(2); + server.readPendingDatagrams(); - udpSocketSend->connectToHost(QHostAddress::Broadcast, 10000); - udpSocketGet->bind(QHostAddress::Any, 10000, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); + for ( unsigned i = 0; i < sockets->size(); ++i ) + { + QObject::connect((*sockets)[i], &Socket::stateChanged, reader, &ConsoleReader::listSockets); + } - udpSocketSend->write(discover); - udpSocketSend->disconnectFromHost(); - delete udpSocketSend; - std::vector *sockets = new std::vector; - - readDiscoverDatagrams(udpSocketGet, sockets); - delete udpSocketGet; - - ConsoleReader reader(sockets); - reader.start(); - - int exitCode = app.exec(); - reader.wait(); - return exitCode; + return app.exec(); } diff --git a/server.cpp b/server.cpp new file mode 100644 index 0000000..d826932 --- /dev/null +++ b/server.cpp @@ -0,0 +1,89 @@ +/************************************************************************* + * Copyright (C) 2015 by Andrius Štikonas * + * * + * This program 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 3 of the License, or * + * (at your option) any later version. * + * * + * 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 .* + *************************************************************************/ + +#include + +#include "consolereader.h" +#include "server.h" + +Server::Server ( std::vector *sockets_vector ) +{ + sockets = sockets_vector; + QUdpSocket *udpSocketSend = new QUdpSocket(); + udpSocketGet = new QUdpSocket(); + + udpSocketSend->connectToHost ( QHostAddress::Broadcast, 10000 ); + udpSocketGet->bind ( QHostAddress::Any, 10000); + + udpSocketSend->write ( discover ); + udpSocketSend->disconnectFromHost(); + delete udpSocketSend; + + connect ( udpSocketGet, &QUdpSocket::readyRead, this, &Server::readPendingDatagrams ); +} + +Server::~Server() +{ + delete udpSocketGet; +} + +void Server::readPendingDatagrams() +{ + while ( udpSocketGet->hasPendingDatagrams() ) + { + QByteArray reply; + reply.resize ( udpSocketGet->pendingDatagramSize() ); + QHostAddress sender; + quint16 senderPort; + + udpSocketGet->readDatagram ( reply.data(), reply.size(), &sender, &senderPort ); + + if ( reply != discover && reply.left ( 2 ) == magicKey ) // check for Magic Key + { + if ( reply.mid ( 4, 2 ) == QByteArray::fromHex ( "71 61" ) ) // Reply to discover packet + { + bool duplicate = false; + for ( std::vector::const_iterator i = sockets->begin() ; i != sockets->end(); ++i ) + { + if ( (*i)->ip == sender ) + { + duplicate = true; + break; + } + } + if ( !duplicate ) + { + Socket *socket = new Socket ( sender, reply ); + sockets->push_back ( socket ); + } + } + else + { + QByteArray mac = reply.mid(6,6); + for ( std::vector::iterator i = sockets->begin() ; i != sockets->end(); ++i ) + { + if ( (*i)->mac == mac ) + { + (*i)->parseReply(reply); + break; + } + + } + } + } + } +} diff --git a/discover.h b/server.h similarity index 52% rename from discover.h rename to server.h index d62f92a..f4556f4 100644 --- a/discover.h +++ b/server.h @@ -15,37 +15,24 @@ * along with this program. If not, see .* *************************************************************************/ +#ifndef SERVER_H +#define SERVER_H + #include "socket.h" -QByteArray discover = QByteArray::fromHex("68 64 00 06 71 61"); - -void readDiscoverDatagrams(QUdpSocket *udpSocketGet, std::vector *sockets) +class Server : public QObject { - while (udpSocketGet->waitForReadyRead(500)) // 500ms - { - while (udpSocketGet->hasPendingDatagrams()) - { - QByteArray datagramGet; - datagramGet.resize(udpSocketGet->pendingDatagramSize()); - QHostAddress sender; - quint16 senderPort; +public: + Server ( std::vector *sockets_vector ); + ~Server(); - udpSocketGet->readDatagram(datagramGet.data(), datagramGet.size(), &sender, &senderPort); + void discoverSockets (); + void readPendingDatagrams(); - if (datagramGet != discover && datagramGet.left(2) == QByteArray::fromHex("68 64")) - { - bool duplicate = false; - for(std::vector::const_iterator i = sockets->begin() ; i != sockets->end(); ++i) - { - if (i->ip == sender) - duplicate = true; - } - if(!duplicate) - { - const Socket socket(sender, datagramGet); - sockets->push_back(socket); - } - } - } - } -} +private: + QByteArray discover = QByteArray::fromHex ( "68 64 00 06 71 61" ); + QUdpSocket *udpSocketGet; + std::vector *sockets; +}; + +#endif /* SERVER_H */ diff --git a/socket.cpp b/socket.cpp index 440679e..858514a 100644 --- a/socket.cpp +++ b/socket.cpp @@ -45,7 +45,25 @@ Socket::Socket ( QHostAddress IPaddress, QByteArray reply ) datagram[PowerOff] = magicKey + QByteArray::fromHex ( "00 17 64 63" ) + mac + twenties + zeros + zero; datagram[TableData] = magicKey + QByteArray::fromHex ( "00 1d" ) + commandID[TableData] + mac + twenties + zeros + QByteArray::fromHex ( "01 00 00" ) + zeros; - datagram[WriteSocketData] = magicKey + QByteArray::fromHex ( "00 a5" ) + commandID[WriteSocketData] + mac + twenties + zeros + QByteArray::fromHex ( "04:00:01")/*table number and unknown*/ + QByteArray::fromHex("8a:00") /* record length = 138 bytes*/ + QByteArray::fromHex (":01:00:43:25" ) + mac + twenties + rmac + twenties + QByteArray::fromHex ( "38:38:38:38:38:38:20:20:20:20:20:20" ) + QByteArray ( "Heater " ) + QByteArray::fromHex ( "04:00:20:00:00:00:14:00:00:00:05:00:00:00:10:27") + fromIP(42,121,111,208) + QByteArray::fromHex("10:27") + "vicenter.orvibo.com" + " " + twenties + twenties + twenties + fromIP(192,168,1,212) + QByteArray::fromHex ( "c0:a8:01:01:ff:ff:ff:00:01:01:00:08:00:ff:00:00" ); + tableData(); + + datagram[WriteSocketData] = magicKey + QByteArray::fromHex ( "00 a5" ) + commandID[WriteSocketData] + mac + twenties + zeros + QByteArray::fromHex ( "04:00:01" ) /*table number and unknown*/ + QByteArray::fromHex ( "8a:00" ) /* record length = 138 bytes*/ + QByteArray::fromHex ( ":01:00:43:25" ) + mac + twenties + rmac + twenties + QByteArray::fromHex ( "38:38:38:38:38:38:20:20:20:20:20:20" ) + QByteArray ( "Heater " ) + QByteArray::fromHex ( "04:00:20:00:00:00:14:00:00:00:05:00:00:00:10:27" ) + fromIP ( 42,121,111,208 ) + QByteArray::fromHex ( "10:27" ) + "vicenter.orvibo.com" + " " + twenties + twenties + twenties + fromIP ( 192,168,1,212 ) + QByteArray::fromHex ( "c0:a8:01:01:ff:ff:ff:00:01:01:00:08:00:ff:00:00" ); + + subscribeTimer = new QTimer(this); + subscribeTimer->setInterval(2*60*1000); + connect(subscribeTimer, &QTimer::timeout, this, &Socket::subscribe); + subscribeTimer->start(); + subscribe(); +} + +Socket::~Socket() +{ + delete subscribeTimer; +} + +void Socket::subscribe() +{ + sendDatagram ( Subscribe ); } void Socket::toggle() @@ -53,21 +71,17 @@ void Socket::toggle() bool powerOld = powered; while ( powerOld == powered ) { - sendDatagram ( Subscribe ); sendDatagram ( powerOld ? PowerOff : PowerOn ); } } void Socket::changeSocketName ( /*QString name*/ ) { - sendDatagram ( Subscribe ); - std::cout << datagram[WriteSocketData].toHex().toStdString(); sendDatagram ( WriteSocketData ); } void Socket::tableData() { - sendDatagram ( Subscribe ); sendDatagram ( TableData ); datagram[SocketData] = magicKey + QByteArray::fromHex ( "00 1d" ) + commandID[SocketData] + mac + twenties + zeros + QByteArray::fromHex ( "04 00 00" ) + zeros; datagram[TimingData] = magicKey + QByteArray::fromHex ( "00 1d" ) + commandID[TimingData] + mac + twenties + zeros + QByteArray::fromHex ( "03 00 00" ) + zeros; @@ -78,38 +92,18 @@ void Socket::tableData() void Socket::sendDatagram ( Datagram d ) { - udpSocketGet = new QUdpSocket(); - udpSocketGet->bind ( QHostAddress::Any, 10000, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint); - - udpSocketSend = new QUdpSocket(); - udpSocketSend->connectToHost ( ip, 10000 ); - udpSocketSend->write ( datagram[d] ); - delete udpSocketSend; - readDatagrams ( udpSocketGet); - delete udpSocketGet; -} - -// FIXME: try to always listen for incoming datagrams to make everything asyncronous -void Socket::readDatagrams ( QUdpSocket *udpSocketGet) -{ - while ( udpSocketGet->waitForReadyRead ( 300 ) ) // 300ms - { - while ( udpSocketGet->hasPendingDatagrams() ) - { - QByteArray reply; - reply.resize ( udpSocketGet->pendingDatagramSize() ); - QHostAddress sender; - quint16 senderPort; - udpSocketGet->readDatagram ( reply.data(), reply.size(), &sender, &senderPort ); - parseReply ( reply ); - } - } + udpSocket = new QUdpSocket(); + udpSocket->connectToHost ( ip, 10000 ); + udpSocket->write ( datagram[d] ); + delete udpSocket; } bool Socket::parseReply ( QByteArray reply ) { if ( reply.left ( 2 ) != magicKey ) + { return false; + } QByteArray id = reply.mid ( 4, 2 ); unsigned int datagram = std::distance ( commandID, std::find ( commandID, commandID + MaxCommands, id ) ); // match commandID with enum @@ -131,14 +125,19 @@ bool Socket::parseReply ( QByteArray reply ) return false; } } - std::cout << reply.toHex().toStdString() << " " << datagram << std::endl; // for debugging purposes only +// std::cout << reply.toHex().toStdString() << " " << datagram << std::endl; // for debugging purposes only switch ( datagram ) { case Subscribe: case PowerOff: case PowerOn: + { + bool poweredOld = powered; powered = reply.right ( 1 ) == one; + if (powered != poweredOld) + Q_EMIT stateChanged(); break; + } case TableData: // FIXME: order might be swapped; socketTableVersion = reply.mid ( reply.indexOf ( QByteArray::fromHex ( "000100000600" ) ) + 6, 2 ); @@ -147,16 +146,20 @@ bool Socket::parseReply ( QByteArray reply ) case SocketData: remotePassword = reply.mid ( reply.indexOf ( rmac + twenties ) + 12, 12 ); name = reply.mid ( reply.indexOf ( rmac + twenties ) + 24, 16 ); + Q_EMIT stateChanged(); break; case TimingData: break; + case WriteSocketData: + Q_EMIT stateChanged(); + break; default: return false; } return true; } -QByteArray Socket::fromIP(unsigned char a, unsigned char b, unsigned char c, unsigned char d) +QByteArray Socket::fromIP ( unsigned char a, unsigned char b, unsigned char c, unsigned char d ) { - return QByteArray::number(a) + QByteArray::number(b) + QByteArray::number(c) + QByteArray::number(d); -} \ No newline at end of file + return QByteArray::number ( a ) + QByteArray::number ( b ) + QByteArray::number ( c ) + QByteArray::number ( d ); +} diff --git a/socket.h b/socket.h index 3900463..b1f2173 100644 --- a/socket.h +++ b/socket.h @@ -20,16 +20,25 @@ #include #include +#include #include -class Socket +const QByteArray magicKey = QByteArray::fromHex ( "68 64" ); // recognize datagrams from the socket + +class Socket : public QObject { +Q_OBJECT + +Q_SIGNALS: + void stateChanged(); + public: - Socket(QHostAddress, QByteArray); + Socket ( QHostAddress, QByteArray ); + ~Socket(); void toggle(); void tableData(); - void changeSocketName(/*QString name*/); - bool parseReply(QByteArray); + void changeSocketName ( /*QString name*/ ); + bool parseReply ( QByteArray ); QHostAddress ip, localIP; QByteArray mac; @@ -39,22 +48,24 @@ public: private: enum Datagram {Subscribe, PowerOff, PowerOn, TableData, SocketData, TimingData, WriteSocketData, MaxCommands}; - void sendDatagram(Datagram); - void readDatagrams(QUdpSocket *udpSocketGet); - QByteArray fromIP(unsigned char, unsigned char, unsigned char, unsigned char); + void sendDatagram ( Datagram ); + QByteArray fromIP ( unsigned char, unsigned char, unsigned char, unsigned char ); + void subscribe(); QByteArray commandID[MaxCommands]; QByteArray datagram[MaxCommands]; QByteArray rmac; // Reveresed mac QByteArray socketTableNumber, socketTableVersion, timingTableNumber, timingTableVersion; - const QByteArray magicKey = QByteArray::fromHex("68 64"); // recognize datagrams from the socket - const QByteArray twenties = QByteArray::fromHex("20 20 20 20 20 20"); // mac address padding - const QByteArray zeros = QByteArray::fromHex("00 00 00 00"); - const QByteArray zero = QByteArray::fromHex("00"); - const QByteArray one = QByteArray::fromHex("01"); + const QByteArray twenties = QByteArray::fromHex ( "20 20 20 20 20 20" ); // mac address padding + const QByteArray zeros = QByteArray::fromHex ( "00 00 00 00" ); + const QByteArray zero = QByteArray::fromHex ( "00" ); + const QByteArray one = QByteArray::fromHex ( "01" ); + + QUdpSocket *udpSocket; + + QTimer *subscribeTimer; - QUdpSocket *udpSocketSend, *udpSocketGet; }; #endif /* SOCKET_H */