2015-01-14 00:50:55 +00:00
/*************************************************************************
* Copyright ( C ) 2015 by Andrius Š tikonas < andrius @ stikonas . eu > *
* *
* 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 < http : //www.gnu.org/licenses/>.*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "socket.h"
2015-01-14 13:55:28 +00:00
# include <algorithm>
2015-01-25 19:39:15 +00:00
# include <iostream>
2015-03-08 11:29:33 +00:00
2015-09-07 15:43:08 +01:00
# include <QDataStream>
2015-03-08 11:29:33 +00:00
# include <QMutex>
2015-02-03 01:24:47 +00:00
# include <QThread>
# include <QUdpSocket>
2015-01-14 13:55:28 +00:00
2015-08-18 15:29:09 +01:00
Socket : : Socket ( QHostAddress IPaddress , QByteArray reply )
2015-01-14 00:50:55 +00:00
{
ip = IPaddress ;
2015-08-18 15:29:09 +01:00
mac = reply . mid ( 7 , 6 ) ;
2015-01-14 13:55:28 +00:00
rmac = mac ;
2015-08-18 15:29:09 +01:00
std : : reverse ( rmac . begin ( ) , rmac . end ( ) ) ;
powered = reply . right ( 1 ) = = one ;
2015-01-25 19:39:15 +00:00
2015-08-18 15:29:09 +01:00
commandID [ QueryAll ] = QStringLiteral ( " qa " ) . toLatin1 ( ) ;
commandID [ Discover ] = QStringLiteral ( " qg " ) . toLatin1 ( ) ; // qg
commandID [ Subscribe ] = QStringLiteral ( " cl " ) . toLatin1 ( ) ; // Login Command
commandID [ PowerOn ] = QStringLiteral ( " sf " ) . toLatin1 ( ) ; // State Flip (change of power state)
2015-01-25 19:39:15 +00:00
commandID [ PowerOff ] = commandID [ PowerOn ] ;
2015-08-18 15:29:09 +01:00
commandID [ ReadTable ] = QStringLiteral ( " rt " ) . toLatin1 ( ) ;
2015-05-27 00:03:33 +01:00
commandID [ SocketData ] = commandID [ ReadTable ] ;
commandID [ TimingData ] = commandID [ ReadTable ] ;
2015-08-18 15:29:09 +01:00
commandID [ TableModify ] = QStringLiteral ( " tm " ) . toLatin1 ( ) ;
QByteArray commandIDPower = QStringLiteral ( " dc " ) . toLatin1 ( ) ; // Socket change responce
2015-01-25 19:39:15 +00:00
// 2 hex bytes are the total length of the message
2015-04-02 22:10:06 +01:00
datagram [ Discover ] = commandID [ Discover ] + mac + twenties ;
2015-02-15 15:11:39 +00:00
datagram [ Subscribe ] = commandID [ Subscribe ] + mac + twenties + rmac + twenties ;
2015-02-21 21:14:22 +00:00
datagram [ PowerOn ] = commandIDPower + mac + twenties + zeros + one ;
datagram [ PowerOff ] = commandIDPower + mac + twenties + zeros + zero ;
2015-08-18 15:29:09 +01:00
datagram [ ReadTable ] = commandID [ ReadTable ] + mac + twenties + /*zeros*/ QByteArray : : fromHex ( " 72 00 00 00 " ) + QByteArray : : fromHex ( " 01 00 00 " ) + zeros ;
2015-01-31 15:28:34 +00:00
2015-02-03 01:24:47 +00:00
udpSocket = new QUdpSocket ( ) ;
2015-08-18 15:29:09 +01:00
udpSocket - > connectToHost ( ip , 10000 ) ;
2015-02-01 01:42:45 +00:00
2015-08-18 15:29:09 +01:00
connect ( this , & Socket : : datagramQueued , this , & Socket : : processQueue ) ;
2015-02-01 01:42:45 +00:00
subscribeTimer = new QTimer ( this ) ;
2015-08-18 15:29:09 +01:00
subscribeTimer - > setInterval ( 2 * 60 * 1000 ) ; // 2 min
2015-02-03 01:24:47 +00:00
subscribeTimer - > setSingleShot ( false ) ;
2015-02-01 01:42:45 +00:00
connect ( subscribeTimer , & QTimer : : timeout , this , & Socket : : subscribe ) ;
subscribeTimer - > start ( ) ;
2015-02-07 17:29:12 +00:00
subscribe ( ) ;
tableData ( ) ;
2015-02-01 01:42:45 +00:00
}
Socket : : ~ Socket ( )
{
delete subscribeTimer ;
2015-02-03 01:24:47 +00:00
delete udpSocket ;
2015-02-01 01:42:45 +00:00
}
2015-08-18 15:29:09 +01:00
void Socket : : sendDatagram ( Datagram d )
2015-02-07 17:29:12 +00:00
{
commands . enqueue ( d ) ;
Q_EMIT datagramQueued ( ) ;
}
void Socket : : run ( )
{
2015-03-08 11:29:33 +00:00
QMutex mutex ;
2015-08-18 15:29:09 +01:00
if ( ! mutex . tryLock ( ) ) {
2015-03-08 11:29:33 +00:00
return ;
2015-03-11 12:34:56 +00:00
}
2015-03-08 11:29:33 +00:00
2015-03-02 12:21:10 +00:00
unsigned short retryCount = 0 ;
QByteArray currentDatagram , previousDatagram = 0 , recordLength ;
2015-08-18 15:29:09 +01:00
while ( commands . size ( ) > 0 ) {
2015-03-02 12:21:10 +00:00
currentDatagram = datagram [ commands . head ( ) ] ;
2015-08-18 15:29:09 +01:00
if ( previousDatagram = = currentDatagram ) {
2015-03-02 12:21:10 +00:00
+ + retryCount ;
2015-03-11 12:34:56 +00:00
}
2015-08-18 15:29:09 +01:00
if ( retryCount = = 5 ) {
2015-03-02 12:21:10 +00:00
std : : cout < < " Stop retrying: " < < currentDatagram . toHex ( ) . toStdString ( ) < < std : : endl ;
commands . dequeue ( ) ;
2015-03-08 11:29:33 +00:00
retryCount = 0 ;
2015-03-02 12:21:10 +00:00
}
2015-02-15 15:11:39 +00:00
QDataStream stream ( & recordLength , QIODevice : : WriteOnly ) ;
stream . setByteOrder ( QDataStream : : BigEndian ) ;
uint16_t length = currentDatagram . length ( ) + 4 ; // +4 for magicKey and total message length
stream < < length ;
2015-08-18 15:29:09 +01:00
udpSocket - > write ( magicKey + recordLength + currentDatagram ) ;
2015-03-08 11:29:33 +00:00
previousDatagram = currentDatagram ;
2015-02-07 17:29:12 +00:00
QThread : : msleep ( 100 ) ;
}
2015-03-08 11:29:33 +00:00
mutex . unlock ( ) ;
2015-02-07 17:29:12 +00:00
}
2015-02-01 01:42:45 +00:00
void Socket : : subscribe ( )
{
2015-08-18 15:29:09 +01:00
sendDatagram ( Subscribe ) ;
2015-01-14 13:55:28 +00:00
}
2015-04-02 22:10:06 +01:00
void Socket : : discover ( )
{
2015-08-18 15:29:09 +01:00
sendDatagram ( Discover ) ;
2015-04-02 22:10:06 +01:00
}
2015-01-26 00:55:20 +00:00
void Socket : : toggle ( )
2015-01-14 13:55:28 +00:00
{
2015-08-18 15:29:09 +01:00
sendDatagram ( powered ? PowerOff : PowerOn ) ;
2015-01-14 13:55:28 +00:00
}
2015-05-26 00:02:19 +01:00
void Socket : : powerOff ( )
{
if ( powered )
2015-08-18 15:29:09 +01:00
sendDatagram ( PowerOff ) ;
2015-05-26 00:02:19 +01:00
Q_EMIT stateChanged ( ) ;
}
void Socket : : powerOn ( )
{
if ( ! powered )
2015-08-18 15:29:09 +01:00
sendDatagram ( PowerOn ) ;
2015-05-26 00:02:19 +01:00
Q_EMIT stateChanged ( ) ;
}
2015-08-18 15:29:09 +01:00
void Socket : : changeSocketName ( QString newName )
2015-01-31 15:28:34 +00:00
{
2015-02-03 01:24:47 +00:00
QByteArray name = newName . toLatin1 ( ) . leftJustified ( 16 , ' ' , true ) ;
2015-08-18 14:25:32 +01:00
writeSocketData ( name , remotePassword , timeZone , countdown ) ;
2015-02-15 12:31:21 +00:00
}
2015-08-18 15:29:09 +01:00
void Socket : : changeSocketPassword ( QString newPassword )
2015-02-15 12:31:21 +00:00
{
QByteArray password = newPassword . toLatin1 ( ) . leftJustified ( 12 , ' ' , true ) ;
2015-08-18 14:25:32 +01:00
writeSocketData ( socketName , password , timeZone , countdown ) ;
2015-02-15 12:31:21 +00:00
}
2015-02-03 01:24:47 +00:00
2015-08-18 15:29:09 +01:00
void Socket : : changeTimezone ( int8_t newTimezone )
2015-02-15 12:31:21 +00:00
{
QByteArray timezone ;
QDataStream stream ( & timezone , QIODevice : : WriteOnly ) ;
stream . setByteOrder ( QDataStream : : BigEndian ) ;
stream < < newTimezone ;
2015-08-18 14:25:32 +01:00
writeSocketData ( socketName , remotePassword , timezone , countdown ) ;
2015-02-15 12:31:21 +00:00
}
2015-08-18 15:29:09 +01:00
void Socket : : setCountDown ( uint16_t countdown )
2015-02-15 12:31:21 +00:00
{
2015-08-18 14:25:32 +01:00
writeSocketData ( socketName , remotePassword , timeZone , countdown ) ;
}
2015-09-04 22:49:13 +01:00
void Socket : : toggleCountDown ( )
{
countdownEnabled = ! countdownEnabled ;
writeSocketData ( socketName , remotePassword , timeZone , countdown ) ;
}
2015-08-18 14:25:32 +01:00
void Socket : : writeSocketData ( QByteArray socketName , QByteArray remotePassword , QByteArray timeZone , uint16_t countdown )
{
2015-11-01 00:31:38 +00:00
QByteArray countDown = intToHex ( countdown ) ;
2015-08-18 14:25:32 +01:00
2015-11-01 00:31:38 +00:00
QByteArray record = QByteArray : : fromHex ( " 01:00 " ) /* record number = 1*/ + versionID + mac + twenties + rmac + twenties + remotePassword + socketName + icon + hardwareVersion + firmwareVersion + wifiFirmwareVersion + port + staticServerIP + port + domainServerName + localIP + localGatewayIP + localNetMask + dhcpNode + discoverable + timeZoneSet + timeZone + ( countdownEnabled ? QByteArray : : fromHex ( " 01:00 " ) : QByteArray : : fromHex ( " 00:ff " ) ) + countDown + zeros + zeros + zeros + QStringLiteral ( " 000000000000000000000000000000 " ) . toLocal8Bit ( ) ;
2015-02-15 15:11:39 +00:00
2015-02-12 23:26:31 +00:00
QByteArray recordLength ;
QDataStream stream ( & recordLength , QIODevice : : WriteOnly ) ;
stream . setByteOrder ( QDataStream : : LittleEndian ) ;
2015-02-15 12:31:21 +00:00
uint16_t length = record . length ( ) ;
2015-02-12 23:26:31 +00:00
stream < < length ;
2015-08-18 15:29:09 +01:00
datagram [ TableModify ] = commandID [ TableModify ] + mac + twenties + zeros + QByteArray : : fromHex ( " 04:00:01 " ) /*table number and unknown*/ + recordLength + record ;
sendDatagram ( TableModify ) ;
2015-01-31 15:28:34 +00:00
}
2015-01-26 00:55:20 +00:00
void Socket : : tableData ( )
{
2015-08-18 15:29:09 +01:00
sendDatagram ( ReadTable ) ;
datagram [ SocketData ] = commandID [ SocketData ] + mac + twenties + zeros + QByteArray : : fromHex ( " 04 00 00 " ) + zeros ;
datagram [ TimingData ] = commandID [ TimingData ] + mac + twenties + zeros + QByteArray : : fromHex ( " 03 00 00 " ) + zeros ;
2015-01-31 15:28:34 +00:00
// table number + 00 + version number
2015-08-18 15:29:09 +01:00
sendDatagram ( SocketData ) ;
sendDatagram ( TimingData ) ;
2015-01-26 00:55:20 +00:00
}
2015-08-18 15:29:09 +01:00
bool Socket : : parseReply ( QByteArray reply )
2015-01-31 15:28:34 +00:00
{
2015-08-18 15:29:09 +01:00
if ( reply . left ( 2 ) ! = magicKey ) {
2015-01-31 15:28:34 +00:00
return false ;
2015-02-01 01:42:45 +00:00
}
2015-01-31 15:28:34 +00:00
2015-08-18 15:29:09 +01:00
QByteArray id = reply . mid ( 4 , 2 ) ;
unsigned int datagram = std : : distance ( commandID , std : : find ( commandID , commandID + MaxCommands , id ) ) ; // match commandID with enum
if ( datagram = = ReadTable ) { // determine the table number
unsigned int table = reply [ reply . indexOf ( twenties ) + 11 ] ;
switch ( table ) {
2015-01-31 15:28:34 +00:00
case 1 :
break ;
case 3 :
datagram = TimingData ;
break ;
case 4 :
datagram = SocketData ;
break ;
2015-03-11 12:34:56 +00:00
case 0 :
qWarning ( ) < < " No table " ; // FIXME: initial data query
2015-01-31 15:28:34 +00:00
default :
2015-03-11 12:34:56 +00:00
qWarning ( ) < < " Failed to identify data table. " ;
2015-05-27 00:03:33 +01:00
datagram = ReadTable ;
2015-01-31 15:28:34 +00:00
return false ;
2015-01-25 19:39:15 +00:00
}
}
2015-08-18 15:29:09 +01:00
switch ( datagram ) {
2015-05-27 00:03:33 +01:00
case QueryAll :
2015-08-18 15:29:09 +01:00
case Discover : {
2015-04-02 22:10:06 +01:00
QByteArray timeArray = reply . right ( 5 ) . left ( 4 ) ;
QDataStream stream ( & timeArray , QIODevice : : ReadOnly ) ;
stream . setByteOrder ( QDataStream : : LittleEndian ) ;
uint32_t time ;
stream > > time ;
socketDateTime . setDate ( QDate ( 1900 , 01 , 01 ) ) ; // midnight 1900-01-01
socketDateTime . setTime ( QTime ( 0 , 0 , 0 ) ) ;
socketDateTime = socketDateTime . addSecs ( time ) ;
}
2015-01-31 15:28:34 +00:00
case Subscribe :
case PowerOff :
2015-08-18 15:29:09 +01:00
case PowerOn : {
2015-02-01 01:42:45 +00:00
bool poweredOld = powered ;
2015-03-08 11:34:01 +00:00
powered = reply . right ( 1 ) = = one ;
2015-08-18 15:29:09 +01:00
if ( powered ! = poweredOld ) {
2015-02-01 01:42:45 +00:00
Q_EMIT stateChanged ( ) ;
2015-03-11 12:34:56 +00:00
}
2015-08-18 15:29:09 +01:00
if ( datagram = = PowerOff & & powered = = true ) { // Required to deque
2015-02-07 17:29:12 +00:00
datagram = PowerOn ;
}
2015-01-31 15:28:34 +00:00
break ;
2015-02-01 01:42:45 +00:00
}
2015-05-27 00:03:33 +01:00
case ReadTable :
2015-01-31 15:28:34 +00:00
// FIXME: order might be swapped;
2015-08-18 15:29:09 +01:00
socketTableVersion = reply . mid ( reply . indexOf ( QByteArray : : fromHex ( " 000100000600 " ) ) + 6 , 2 ) ;
2015-01-31 15:28:34 +00:00
// 000100000600
break ;
2015-08-18 15:29:09 +01:00
case SocketData : {
2015-03-08 11:29:33 +00:00
// std::cout << reply.toHex().toStdString() << " " << datagram << std::endl; // for debugging purposes only
2015-08-18 15:29:09 +01:00
unsigned short int index = reply . indexOf ( rmac + twenties ) ;
versionID = reply . mid ( index - 14 , 2 ) ;
2015-02-09 21:09:50 +00:00
index + = 12 ; // length of rmac + padding
2015-08-18 15:29:09 +01:00
remotePassword = reply . mid ( index , 12 ) ; // max 12 symbols
2015-02-09 21:09:50 +00:00
index + = 12 ;
2015-08-18 15:29:09 +01:00
socketName = reply . mid ( index , 16 ) ; // max 16 symbols
2015-02-09 21:09:50 +00:00
index + = 16 ;
2015-08-18 15:29:09 +01:00
icon = reply . mid ( index , 2 ) ;
2015-02-09 21:09:50 +00:00
index + = 2 ;
2015-08-18 15:29:09 +01:00
hardwareVersion = reply . mid ( index , 4 ) ;
2015-02-09 21:09:50 +00:00
index + = 4 ;
2015-08-18 15:29:09 +01:00
firmwareVersion = reply . mid ( index , 4 ) ;
2015-02-09 21:09:50 +00:00
index + = 4 ;
2015-08-18 15:29:09 +01:00
wifiFirmwareVersion = reply . mid ( index , 4 ) ;
2015-02-11 00:33:04 +00:00
index + = 6 ;
2015-08-18 15:29:09 +01:00
staticServerIP = reply . mid ( index , 4 ) ; // 42.121.111.208 is used
2015-02-15 15:26:37 +00:00
index + = 6 ;
2015-08-18 15:29:09 +01:00
domainServerName = reply . mid ( index , 40 ) ;
2015-02-15 15:26:37 +00:00
index + = 40 ;
2015-08-18 15:29:09 +01:00
localIP = reply . mid ( index , 4 ) ;
2015-02-11 00:33:04 +00:00
index + = 4 ;
2015-08-18 15:29:09 +01:00
localGatewayIP = reply . mid ( index , 4 ) ;
2015-02-11 14:31:20 +00:00
index + = 4 ;
2015-08-18 15:29:09 +01:00
localNetMask = reply . mid ( index , 4 ) ;
2015-02-11 14:31:20 +00:00
index + = 4 ;
2015-08-18 15:29:09 +01:00
dhcpNode = reply . mid ( index , 1 ) ;
2015-02-11 14:31:20 +00:00
+ + index ;
2015-08-18 15:29:09 +01:00
discoverable = reply . mid ( index , 1 ) ;
2015-02-11 14:31:20 +00:00
+ + index ;
2015-08-18 15:29:09 +01:00
timeZoneSet = reply . mid ( index , 1 ) ;
2015-02-11 14:31:20 +00:00
+ + index ;
2015-08-18 15:29:09 +01:00
timeZone = reply . mid ( index , 1 ) ;
2015-02-11 14:31:20 +00:00
+ + index ;
2015-08-18 15:29:09 +01:00
countdownEnabled = reply . mid ( index , 2 ) = = QByteArray : : fromHex ( " 01:00 " ) ;
2015-08-18 14:25:32 +01:00
index + = 2 ;
2015-08-18 15:29:09 +01:00
QByteArray countDown = reply . mid ( index , 2 ) ;
2015-08-18 14:25:32 +01:00
QDataStream stream ( & countDown , QIODevice : : ReadOnly ) ;
stream . setByteOrder ( QDataStream : : LittleEndian ) ;
stream > > countdown ;
2015-02-01 01:42:45 +00:00
Q_EMIT stateChanged ( ) ;
2015-01-31 15:28:34 +00:00
break ;
2015-02-09 21:09:50 +00:00
}
2015-01-31 15:28:34 +00:00
case TimingData :
2015-04-02 22:56:57 +01:00
// std::cout << reply.toHex().toStdString() << " " << datagram << std::endl; // for debugging purposes only
2015-01-31 15:28:34 +00:00
break ;
2015-05-27 00:03:33 +01:00
case TableModify :
2015-08-18 15:29:09 +01:00
sendDatagram ( SocketData ) ;
2015-02-01 01:42:45 +00:00
break ;
2015-01-31 15:28:34 +00:00
default :
return false ;
}
2015-02-07 17:29:12 +00:00
2015-08-18 15:29:09 +01:00
if ( commands . size ( ) > 0 ) {
if ( datagram = = commands . head ( ) ) {
2015-02-07 17:29:12 +00:00
commands . dequeue ( ) ;
}
}
2015-01-31 15:28:34 +00:00
return true ;
2015-01-14 13:55:28 +00:00
}
2015-11-01 00:31:38 +00:00
QByteArray Socket : : intToHex ( uint16_t decimal ) {
QByteArray hex ;
QDataStream stream1 ( & hex , QIODevice : : WriteOnly ) ;
stream1 . setByteOrder ( QDataStream : : LittleEndian ) ;
stream1 < < decimal ;
return hex ;
}