Merge pull request #69 from QuasarApp/fixSync2

fix sync
This commit is contained in:
Andrei Yankovich 2018-08-16 22:45:35 +03:00 committed by GitHub
commit 7b6e435460
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1116 additions and 447 deletions

View File

@ -34,7 +34,9 @@ SOURCES += main.cpp \
serverlistmodel.cpp \ serverlistmodel.cpp \
playlistsmodel.cpp \ playlistsmodel.cpp \
currentplaylistmodel.cpp \ currentplaylistmodel.cpp \
../sync/playlist.cpp ../sync/playlist.cpp \
../sync/syncpackage.cpp \
../sync/basepackage.cpp
RESOURCES += qml.qrc RESOURCES += qml.qrc
@ -71,5 +73,7 @@ HEADERS += \
serverlistmodel.h \ serverlistmodel.h \
playlistsmodel.h \ playlistsmodel.h \
currentplaylistmodel.h \ currentplaylistmodel.h \
../sync/playlist.h ../sync/playlist.h \
../sync/syncpackage.h \
../sync/basepackage.h

View File

@ -1,7 +1,5 @@
#include "currentplaylistmodel.h" #include "currentplaylistmodel.h"
using namespace syncLib;
CurrentPlayListModel::CurrentPlayListModel(QObject *parent) : CurrentPlayListModel::CurrentPlayListModel(QObject *parent) :
QAbstractListModel(parent), QAbstractListModel(parent),
syncEngine(nullptr), syncEngine(nullptr),
@ -47,13 +45,13 @@ void CurrentPlayListModel::fetchMore(const QModelIndex & /* index */)
int remainder = playList->size() - itemCount; int remainder = playList->size() - itemCount;
int itemsToFetch = qMin(100, remainder); int itemsToFetch = qMin(100, remainder);
if(itemsToFetch < 0){ if (itemsToFetch < 0) {
beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 );
itemCount += itemsToFetch; itemCount += itemsToFetch;
endRemoveRows(); endRemoveRows();
}else{ } else if (itemsToFetch > 0) {
beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1);
itemCount += itemsToFetch; itemCount += itemsToFetch;
@ -78,10 +76,8 @@ QVariant CurrentPlayListModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case nameRole: case nameRole:
return playList->at(index.row()).name; return playList->at(index.row()).name;
break;
case idRole: case idRole:
return playList->at(index.row()).id; return playList->at(index.row()).id;
break;
default: default:
break; break;
} }

View File

@ -17,7 +17,7 @@ class CurrentPlayListModel : public QAbstractListModel
private: private:
SyncEngine * syncEngine; SyncEngine * syncEngine;
const QList<syncLib::SongStorage> *playList; const QList<SongStorage> *playList;
int itemCount; int itemCount;
private slots: private slots:

View File

@ -1,7 +1,5 @@
#include "playlistmodel.h" #include "playlistmodel.h"
using namespace syncLib;
PlayListModel::PlayListModel(QObject *parent) : PlayListModel::PlayListModel(QObject *parent) :
QAbstractListModel(parent), QAbstractListModel(parent),
syncEngine(nullptr) syncEngine(nullptr)
@ -55,7 +53,7 @@ void PlayListModel::fetchMore(const QModelIndex & /* index */)
itemCount += itemsToFetch; itemCount += itemsToFetch;
endRemoveRows(); endRemoveRows();
}else{ }else if (itemsToFetch > 0){
beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1);
itemCount += itemsToFetch; itemCount += itemsToFetch;
@ -94,7 +92,7 @@ QVariant PlayListModel::data(const QModelIndex &index, int role) const
bool PlayListModel::select(int id){ bool PlayListModel::select(int id){
for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->id == id){ if(i->id == id){
if((i->isSelected = !i->isSelected)){ if((i->isSelected = !i->isSelected)){
@ -114,7 +112,7 @@ bool PlayListModel::select(int id){
QList<int> PlayListModel::getSelected(){ QList<int> PlayListModel::getSelected(){
QList<int> result; QList<int> result;
for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->isSelected){ if(i->isSelected){
result.push_back(i->id); result.push_back(i->id);
} }
@ -124,7 +122,7 @@ QList<int> PlayListModel::getSelected(){
bool PlayListModel::isSelected(int id){ bool PlayListModel::isSelected(int id){
for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->id == id){ if(i->id == id){
return i->isSelected; return i->isSelected;
} }

View File

@ -16,7 +16,7 @@ class PlayListModel : public QAbstractListModel
private: private:
SyncEngine * syncEngine; SyncEngine * syncEngine;
QList<syncLib::SongStorage> playList; QList<SongStorage> playList;
QString playListName; QString playListName;
int itemCount; int itemCount;

View File

@ -42,13 +42,13 @@ void PlayListsModel::fetchMore(const QModelIndex & /* index */)
int remainder = playLists.size() - itemCount; int remainder = playLists.size() - itemCount;
int itemsToFetch = qMin(100, remainder); int itemsToFetch = qMin(100, remainder);
if(itemsToFetch < 0){ if (itemsToFetch < 0) {
beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 );
itemCount += itemsToFetch; itemCount += itemsToFetch;
endRemoveRows(); endRemoveRows();
}else{ } else if (itemsToFetch > 0) {
beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1);
itemCount += itemsToFetch; itemCount += itemsToFetch;

View File

@ -41,13 +41,13 @@ void ServerListModel::fetchMore(const QModelIndex & /* index */)
int remainder = servers->size() - itemCount; int remainder = servers->size() - itemCount;
int itemsToFetch = qMin(100, remainder); int itemsToFetch = qMin(100, remainder);
if(itemsToFetch < 0){ if (itemsToFetch < 0) {
beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 ); beginRemoveRows(QModelIndex(), 0, 0 - itemsToFetch - 1 );
itemCount += itemsToFetch; itemCount += itemsToFetch;
endRemoveRows(); endRemoveRows();
}else{ } else if (itemsToFetch > 0) {
beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1); beginInsertRows(QModelIndex(), itemCount, itemCount + itemsToFetch - 1);
itemCount += itemsToFetch; itemCount += itemsToFetch;

View File

@ -4,7 +4,7 @@
SyncEngine::SyncEngine() SyncEngine::SyncEngine()
{ {
sync = new syncLib::Sync(); sync = new Sync();
sqlApi = sync->getSqlApi(); sqlApi = sync->getSqlApi();
connect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged())); connect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged()));
@ -44,7 +44,7 @@ bool SyncEngine::init(){
return true; return true;
} }
const QList<syncLib::SongStorage>* SyncEngine::currentPlayList() const{ const QList<SongStorage>* SyncEngine::currentPlayList() const{
return sync->getPlayList(); return sync->getPlayList();
} }
@ -196,7 +196,7 @@ bool SyncEngine::addSong(const QString &songUrl){
} }
bool SyncEngine::removeSong(int id){ bool SyncEngine::removeSong(int id){
syncLib::SongHeader header; SongHeader header;
header.id = id; header.id = id;
if(!sqlApi->removeSong(header)) if(!sqlApi->removeSong(header))
return false; return false;
@ -223,7 +223,7 @@ bool SyncEngine::removePlayList(const QString &name){
bool SyncEngine::addToPlayList(int id, const QString &playList){ bool SyncEngine::addToPlayList(int id, const QString &playList){
syncLib::SongHeader header; SongHeader header;
header.id = id; header.id = id;
if(!sqlApi->addToPlayList(header, playList)){ if(!sqlApi->addToPlayList(header, playList)){
@ -239,7 +239,7 @@ bool SyncEngine::addToPlayList(int id, const QString &playList){
bool SyncEngine::removeFromPlayList(int id, const QString &playList){ bool SyncEngine::removeFromPlayList(int id, const QString &playList){
syncLib::SongHeader header; SongHeader header;
header.id = id; header.id = id;
if(!sqlApi->removeFromPlayList(header, playList)){ if(!sqlApi->removeFromPlayList(header, playList)){

View File

@ -20,8 +20,8 @@ class SyncEngine : public QObject
private: private:
syncLib::Sync *sync; Sync *sync;
syncLib::MySql * sqlApi; MySql * sqlApi;
QString _lastError; QString _lastError;
QSettings settings; QSettings settings;

View File

@ -13,7 +13,7 @@ ETcpSocket::ETcpSocket(QTcpSocket*ptr)
init(); init();
} }
ETcpSocket::ETcpSocket(const QString& address, int port){ ETcpSocket::ETcpSocket(const QString& address, unsigned short port){
source = new QTcpSocket(); source = new QTcpSocket();
source->connectToHost(address, port); source->connectToHost(address, port);
if(!source->waitForConnected(DEEP_SCANER_INTERVAL) || !source->open(QIODevice::ReadWrite)){ if(!source->waitForConnected(DEEP_SCANER_INTERVAL) || !source->open(QIODevice::ReadWrite)){
@ -24,14 +24,134 @@ ETcpSocket::ETcpSocket(const QString& address, int port){
void ETcpSocket::init(){ void ETcpSocket::init(){
array = new QByteArray; array = new QByteArray;
time = 0;
fSynced = false;
connect(source,SIGNAL(connected()),this,SLOT(connected_())); connect(source, SIGNAL(connected()), this, SLOT(connected_()));
connect(source,SIGNAL(disconnected()),this,SLOT(disconnected_())); connect(source, SIGNAL(disconnected()), this, SLOT(disconnected_()));
connect(source,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_(QAbstractSocket::SocketError))); connect(source, SIGNAL(error(QAbstractSocket::SocketError)),
connect(source,SIGNAL(hostFound()),this,SLOT(hostFound_())); this, SLOT(error_(QAbstractSocket::SocketError)));
connect(source,SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),this,SLOT(proxyAuthenticationRequired_(const QNetworkProxy &, QAuthenticator *))); connect(source, SIGNAL(hostFound()), this,SLOT(hostFound_()));
connect(source,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(stateChanged_(QAbstractSocket::SocketState))); connect(source,
connect(source,SIGNAL(readyRead()),this,SLOT(readReady_())); SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
this, SLOT(proxyAuthenticationRequired_(const QNetworkProxy &, QAuthenticator *)));
connect(source, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this ,SLOT(stateChanged_(QAbstractSocket::SocketState)));
connect(source, SIGNAL(readyRead()), this, SLOT(readReady_()));
}
bool ETcpSocket::_driverResponse(const SyncPackage& from) {
if(!from.isValid()){
return false;
}
SyncPackage pac;
switch (from.type) {
case t_Header:
syncList.clear();
precisionSync = from.getPrecision();
lastTime = ChronoTime::now();
pac.sourceBytes = ChronoTime::now();
pac.nativeTime = from.getTime();
pac.type = t_Source;
pac.firstByte = 0;
_Write(pac.parseTo(), true);
syncList[0] = pac;
break;
case t_Source:
syncList[from.getIndex()] = from;
pac.type = t_Responce;
pac.firstByte = from.getIndex();
pac.sourceBytes = ChronoTime::now();
_Write(pac.parseTo(), true);
break;
case t_Responce:
syncList[from.getIndex()].ping = ChronoTime::now() - lastTime;
lastTime = ChronoTime::now();
if(syncList.size() >= precisionSync){
pac.type = t_End;
auto ping = syncList.first().ping;
auto index = syncList.begin();
for (auto i = syncList.begin(); i != syncList.end(); i++) {
if (i.value().ping < ping) {
ping = i.value().ping;
index = i;
}
}
pac.firstByte = index->firstByte;
pac.sourceBytes = index->ping;
pac.nativeTime = index->nativeTime;
_Write(pac.parseTo(), true);
return true;
}
pac.type = t_Source;
pac.firstByte = from.getIndex() + 1;
pac.sourceBytes = ChronoTime::now();
pac.nativeTime = from.getTime();
_Write(pac.parseTo(), true);
syncList[pac.firstByte] = pac;
break;
case t_End: {
if(syncList.size() <= from.getIndex()){
return false;
}
auto ping = from.getPing();
if (ping > 10) {
return false;
}
time = from.getNative() - syncList[from.getIndex()].getTime() - from.getPing() / 2;
fSynced = true;
qDebug() << "syncTime :" << time;
emit synced();
break;
}
default:
break;
}
return true;
}
void ETcpSocket::_driverStart() {
syncList.clear();
SyncPackage pac;
precisionSync = SYNC_COUNT;
pac.type = t_Header;
pac.firstByte = precisionSync;
pac.sourceBytes = ChronoTime::now();
_Write(pac.parseTo(), true);
}
void ETcpSocket::_driver(QByteArray *data){
SyncPackage pac;
if(!pac.parseFrom(*data)){
return;
}
_driverResponse(pac);
} }
void ETcpSocket::error_(QAbstractSocket::SocketError i){ void ETcpSocket::error_(QAbstractSocket::SocketError i){
@ -70,9 +190,16 @@ void ETcpSocket::readReady_(){
qDebug()<<"messae size:" << size; qDebug()<<"messae size:" << size;
qDebug()<<"message package size:" << array->size(); qDebug()<<"message package size:" << array->size();
#endif #endif
if(size==array->size()) if(size == array->size())
{ {
array->remove(0, sizeof(qint32)); array->remove(0, sizeof(qint32));
if(array->back()){
_driver(array);
delete array;
array = new QByteArray();
return;
}
array->remove(array->size() - 1, 1);
ReadyStack.push_back(array); ReadyStack.push_back(array);
array=new QByteArray(); array=new QByteArray();
emit Message(this); emit Message(this);
@ -97,13 +224,31 @@ QString ETcpSocket::localName() const{
QByteArray* ETcpSocket::topStack(){ QByteArray* ETcpSocket::topStack(){
if(ReadyStack.size()) if(ReadyStack.size())
return ReadyStack.front(); return ReadyStack.front();
return NULL; return nullptr;
}
milliseconds ETcpSocket::getTime()const{
return time;
} }
QTcpSocket* ETcpSocket::getSource()const{ QTcpSocket* ETcpSocket::getSource()const{
return source; return source;
} }
void ETcpSocket::sync(){
if(fSynced){
return;
}
_driverStart();
}
bool ETcpSocket::isSynced()const{
return fSynced;
}
void ETcpSocket::nextItem(bool free){ void ETcpSocket::nextItem(bool free){
if( ReadyStack.size()){ if( ReadyStack.size()){
if(free){ if(free){
@ -122,13 +267,18 @@ QString ETcpSocket::toStringTcp(){
return source->peerAddress().toString(); return source->peerAddress().toString();
} }
bool ETcpSocket::Write(const QByteArray&data){ bool ETcpSocket::Write(const QByteArray &data){
return _Write(data);
}
bool ETcpSocket::_Write(const QByteArray&data, bool isDriver){
if(source->state()==QTcpSocket::ConnectedState){ if(source->state()==QTcpSocket::ConnectedState){
QByteArray array; QByteArray array;
QDataStream stream(&array, QIODevice::ReadWrite); QDataStream stream(&array, QIODevice::ReadWrite);
stream << qint32(0); stream << qint32(0);
array.append(data); array.append(data);
array.append(qint8(isDriver));
stream.device()->seek(0); stream.device()->seek(0);
stream<<qint32(array.size()); stream<<qint32(array.size());
@ -144,12 +294,19 @@ bool ETcpSocket::Write(const QByteArray&data){
return false; return false;
} }
bool ETcpSocket::isValid(){
return source->isValid() && source->isOpen();
}
ETcpSocket::~ETcpSocket() ETcpSocket::~ETcpSocket()
{ {
for(QByteArray*i:ReadyStack){ for(QByteArray*i:ReadyStack){
i->clear(); i->clear();
delete i; delete i;
} }
syncList.clear();
disconnect(source,SIGNAL(connected()),this,SLOT(connected_())); disconnect(source,SIGNAL(connected()),this,SLOT(connected_()));
disconnect(source,SIGNAL(disconnected()),this,SLOT(disconnected_())); disconnect(source,SIGNAL(disconnected()),this,SLOT(disconnected_()));
disconnect(source,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_(QAbstractSocket::SocketError))); disconnect(source,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_(QAbstractSocket::SocketError)));

View File

@ -4,7 +4,7 @@
#include <QTcpServer> #include <QTcpServer>
#include <QList> #include <QList>
#include <QDataStream> #include <QDataStream>
#include "chronotime.h" #include "syncpackage.h"
/** /**
@ -36,9 +36,41 @@ private:
QTcpSocket *source; QTcpSocket *source;
QByteArray *array; QByteArray *array;
qint32 size; qint32 size;
milliseconds time;
milliseconds lastTime;
char precisionSync;
QList<QByteArray*> ReadyStack; QList<QByteArray*> ReadyStack;
bool fSynced;
QMap<char ,SyncPackage> syncList;
void init(); void init();
/**
* @brief _driverResponse
* @param pac
* @return true if all done
*/
bool _driverResponse(const SyncPackage &from);
/**
* @brief _driverResponse
* @param pac
* @return true if all done
*/
void _driverStart();
/**
* @brief _driver
* @return true is package of Driver
*/
void _driver(QByteArray*);
/**
* @brief Write - sends a message to the network.
* @param isDriver - flag of driver info
* @return true if all done else false.
*/
bool _Write(const QByteArray&, bool isDriver = false);
private slots: private slots:
void connected_(); void connected_();
@ -51,7 +83,18 @@ private slots:
public: public:
explicit ETcpSocket(); explicit ETcpSocket();
explicit ETcpSocket(QTcpSocket*); explicit ETcpSocket(QTcpSocket*);
explicit ETcpSocket(const QString& addres,int port); explicit ETcpSocket(const QString& addres, unsigned short port);
/**
* @brief sync
*/
void sync();
/**
* @brief isSynced
* @return true
*/
bool isSynced()const;
/** /**
* @brief setCheckInterval - set new interval of chking ping * @brief setCheckInterval - set new interval of chking ping
@ -66,10 +109,15 @@ public:
int getCheckInterval()const; int getCheckInterval()const;
/** /**
* @brief getPing * @brief setTime set new Time
* @return ping of soccket;
*/ */
int getPing()const; void setTime(milliseconds newTime);
/**
* @brief getTime
* @return time of soccket;
*/
milliseconds getTime()const;
/** /**
* @brief getSource * @brief getSource
* @return Qt TCP socket * @return Qt TCP socket
@ -92,6 +140,13 @@ public:
* @return size of Descript of Packege * @return size of Descript of Packege
*/ */
int sizeDescriptPackege(); int sizeDescriptPackege();
/**
* @brief isValid
* @return true if socket active;
*/
bool isValid();
/** /**
* @brief Write - sends a message to the network. * @brief Write - sends a message to the network.
* @return true if all done else false. * @return true if all done else false.
@ -163,6 +218,12 @@ signals:
void StateChanged(ETcpSocket*,QAbstractSocket::SocketState socketState); void StateChanged(ETcpSocket*,QAbstractSocket::SocketState socketState);
/**
* @brief synced emited when host is synced
*/
void synced();
}; };
#endif // CLIENT_H #endif // CLIENT_H

118
sync/basepackage.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "basepackage.h"
package::package()
{
clear();
}
package::package( QByteArray &array):
package::package(){
parseFrom(array);
}
const SongHeader& package::getHeader() const{
return header;
}
const Song& package::getSong() const{
return source;
}
const Syncer& package::getPlayData() const{
return playdata;
}
const Type& package::getType() const{
return type;
}
bool package::isValid() const{
bool ret = true;
if(type == TypePackage::t_void){
return false;
}
if(type & TypePackage::t_sync && type & t_brodcaster){
ret = ret && playdata.seek > 0 && playdata.timeOn > 0;
}
if(type & TypePackage::t_song_h && type & t_brodcaster){
ret = ret && header.size > 0;
}
if(type & TypePackage::t_song && type & t_brodcaster){
ret = ret && source.size > 0;
}
return ret;
}
void package::clear(){
type = TypePackage::t_void;
source.clear();
playdata.seek = 0;
}
QByteArray package::parseTo(){
QByteArray temp;
QDataStream stream(&temp, QIODevice::WriteOnly);
temp.clear();
if(isValid()){
stream << static_cast<unsigned char>(type);
if(type & TypePackage::t_sync && type & t_brodcaster){
stream << playdata.seek;
stream << playdata.timeOn;
}
if(type & TypePackage::t_song_h && type & t_brodcaster){
stream << header;
}
if(type & TypePackage::t_song && type & t_brodcaster){
stream << source;
}
}
return temp;
}
bool package::parseFrom(QByteArray &array){
type = TypePackage::t_void;
QDataStream stream(&array, QIODevice::ReadOnly);
unsigned char temp_type;
stream >> temp_type;
type = static_cast<TypePackage> (temp_type);
if(type & TypePackage::t_sync){
stream >> playdata.seek;
stream >> playdata.timeOn;
}
if(type & TypePackage::t_song_h){
stream >> header;
}
if(type & TypePackage::t_song){
stream >> source;
}
return isValid();
}
package::~package(){}

112
sync/basepackage.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef BASEPACKAGE_H
#define BASEPACKAGE_H
#include "song.h"
#include "config.h"
#include <QByteArray>
typedef unsigned char Type;
/**
* @brief The TypePackage enum
* t_void = this package empty and not valid.
* t_play = play current audio file.
* t_song_h = the header of playing audio file.
* t_song = the package with this type is necessary for translite media data on network.
* t_sync = the infomation about sync playning media file on network.
* t_close = the information about close channel.
* t_syncTime = getLocalTime of socket
* t_what = request for information about the node
* t_brodcaster = information about the node
*/
enum TypePackage{
t_void = 0x00,
t_play = 0x01,
t_song_h = 0x02,
t_song = 0x04,
t_sync = 0x08,
t_close = 0x10,
t_pause = 0x20,
t_what = 0x40,
t_brodcaster = 0x80
};
/**
* @brief The package class. Package for translite media data on network
*
* parse map:
* 1 byle - type
* data
*/
class package
{
private:
Type type;
Song source;
SongHeader header;
bool fbroadcaster;
Syncer playdata;
public:
package();
package(QByteArray &array);
~package();
/**
* @brief getHeader
* @return Header of the song
*/
const SongHeader& getHeader() const;
/**
* @brief getSong
* @return Song
*/
const Song& getSong() const;
/**
* @brief getPlayTime
* @return time of playning media data
*/
const Syncer &getPlayData() const;
/**
* @brief getType
* @return type of package
*/
const Type& getType() const;
/**
* @brief getTime
* @return time of sended package pc
*/
const milliseconds& getTime()const;
/**
* @brief isValid
* @return true if package is valid
*/
bool isValid() const;
/**
* @brief clear all date of package
*/
void clear();
/**
* @brief parseTo parse this package to byte array
* @return byte array
*/
QByteArray parseTo();
/**
* @brief parseFrom create a package from bytes
* @param array of bytes
* @return true if package valid
*/
bool parseFrom(QByteArray& array);
friend class Sync;
};
#endif // BASEPACKAGE_H

View File

@ -1,24 +1,32 @@
#include "chronotime.h" #include "chronotime.h"
#include <QDateTime>
#include <QDebug> #include <QDebug>
ChronoTime::ChronoTime()
{
}
/* /*
* information about chrono * information about chrono
* https://stackoverflow.com/questions/31255486/c-how-do-i-convert-a-stdchronotime-point-to-long-and-back * https://stackoverflow.com/questions/31255486/c-how-do-i-convert-a-stdchronotime-point-to-long-and-back
*/ */
milliseconds ChronoTime::stdTime() {
milliseconds ChronoTime::now(int calibration){
auto tim = std::chrono::system_clock::now(); auto tim = std::chrono::system_clock::now();
auto mc = std::chrono::time_point_cast<std::chrono::milliseconds>(tim); auto mc = std::chrono::time_point_cast<std::chrono::milliseconds>(tim);
auto epoh = mc.time_since_epoch(); auto epoh = mc.time_since_epoch();
#ifdef QT_DEBUG #ifdef QT_DEBUG
qDebug() << epoh.count(); qDebug() << epoh.count();
#endif #endif
return epoh.count() + calibration; return epoh.count();
}
milliseconds ChronoTime::qtTime() {
return QDateTime::currentMSecsSinceEpoch();
}
ChronoTime::ChronoTime()
{
}
milliseconds ChronoTime::now(milliseconds calibration){
return qtTime() + calibration;
} }
Clock ChronoTime::from(const milliseconds& mc){ Clock ChronoTime::from(const milliseconds& mc){

View File

@ -11,13 +11,16 @@ typedef std::chrono::time_point<std::chrono::high_resolution_clock> Clock;
class ChronoTime class ChronoTime
{ {
private :
static milliseconds stdTime();
static milliseconds qtTime();
public: public:
ChronoTime(); ChronoTime();
/** /**
* @brief now - get now time on microsecunds * @brief now - get now time on microsecunds
* @return - count of microsecunds * @return - count of microsecunds
*/ */
static milliseconds now(int calibration = 0); static milliseconds now(milliseconds calibration = 0);
/** /**
* @brief from cast to chrono secunds * @brief from cast to chrono secunds
* @param mcrs microseconds of uint_64 * @param mcrs microseconds of uint_64

View File

@ -25,8 +25,8 @@
#define RESYNC_TIME 1000 // 1 sec on millisec #define RESYNC_TIME 1000 // 1 sec on millisec
#define MAX_RESYNC_COUNT 3 #define MAX_RESYNC_COUNT 3
#define SYNC_TIME 5 * 1000 // 5 sec on millisec #define SYNC_TIME 5 * 1000 // 5 sec on millisec
#define SYNC_COUNT 20
#define DEEP_SCANER_INTERVAL 1000 // 1 sec #define DEEP_SCANER_INTERVAL 1000 // 1 sec
#define CHECK_PING_INTERVAL 5 * 60 *1000// 5 minutes
// sync // sync
#define MIN_DIFFERENCE 10 // millisec #define MIN_DIFFERENCE 10 // millisec

View File

@ -5,8 +5,6 @@
#include <QSettings> #include <QSettings>
#include "playlist.h" #include "playlist.h"
namespace syncLib{
MySql::MySql(const QString &databasename): MySql::MySql(const QString &databasename):
db(nullptr), db(nullptr),
qyery(nullptr) qyery(nullptr)
@ -119,43 +117,7 @@ void MySql::initDB(const QString &database){
} }
} }
bool MySql::find(const QMediaContent &song, SongStorage &response){ bool MySql::find(const SongHeader &song, SongStorage &response){
QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){
return false;
}
for(SongStorage &i: songs){
if(i == song){
response = i;
return true;
}
}
return false;
}
bool MySql::find(const QMediaContent &song, SongHeader &response){
QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){
return false;
}
for(SongStorage &i: songs){
if(i == song){
response = (SongHeader&)i;
return true;
}
}
return false;
}
bool MySql::find(const SongHeader &song, QMediaContent &response){
QList<SongStorage> songs; QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){ if(!updateAvailableSongs(songs)){
@ -164,7 +126,7 @@ bool MySql::find(const SongHeader &song, QMediaContent &response){
for(SongStorage &i: songs){ for(SongStorage &i: songs){
if((SongHeader&)i == song){ if((SongHeader&)i == song){
response = i.toMedia(); response = i;
return true; return true;
} }
} }
@ -182,6 +144,9 @@ bool MySql::saveToStorage(QUrl &url, const Song &song) const{
return false; return false;
} }
QDir dir;
dir.mkpath(songDir);
QFile file(songDir + "/" + song.name); QFile file(songDir + "/" + song.name);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)){ if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
@ -510,5 +475,5 @@ MySql::~MySql(){
QSqlDatabase::removeDatabase(dataBaseName); QSqlDatabase::removeDatabase(dataBaseName);
} }
}

View File

@ -6,8 +6,6 @@
class QSqlDatabase; class QSqlDatabase;
class QSqlQuery; class QSqlQuery;
namespace syncLib {
class MySql class MySql
{ {
private: private:
@ -43,23 +41,7 @@ public:
* @param response Media Content of finded song * @param response Media Content of finded song
* @return true if song finded * @return true if song finded
*/ */
bool find(const SongHeader& song, QMediaContent& response); bool find(const SongHeader& song, SongStorage &response);
/**
* @brief find - find song
* @param song - song header
* @param response Media Content of finded song
* @return true if song finded
*/
bool find(const QMediaContent& song, SongHeader& response);
/**
* @brief find - find song
* @param song - media Content
* @param response header of finded song
* @return true if song finded
*/
bool find(const QMediaContent& song, SongStorage &response);
/** /**
* @brief setSoundDir * @brief setSoundDir
@ -194,5 +176,4 @@ public:
~MySql(); ~MySql();
}; };
}
#endif // MYSQL_H #endif // MYSQL_H

View File

@ -2,123 +2,10 @@
#include "exaptions.h" #include "exaptions.h"
#include "LocalScanner.h" #include "LocalScanner.h"
namespace syncLib{
package::package()
{
clear();
}
package::package( QByteArray &array):
package::package(){
parseFrom(array);
}
const SongHeader& package::getHeader() const{
return header;
}
const Song& package::getSong() const{
return source;
}
const Syncer& package::getPlayData() const{
return playdata;
}
const Type& package::getType() const{
return type;
}
bool package::isValid() const{
bool ret = true;
if(type == TypePackage::t_void){
return false;
}
if(type & TypePackage::t_sync && type & t_brodcaster){
ret = ret && (playdata.seek > 0);
}
if(type & TypePackage::t_song_h && type & t_brodcaster){
ret = ret && header.size > 0;
}
if(type & TypePackage::t_song && type & t_brodcaster){
ret = ret && source.size > 0;
}
return ret;
}
void package::clear(){
type = TypePackage::t_void;
source.clear();
playdata.seek = 0;
}
QByteArray package::parseTo(){
QByteArray temp;
QDataStream stream(&temp, QIODevice::WriteOnly);
temp.clear();
if(isValid()){
stream << static_cast<unsigned char>(type);
if(type & TypePackage::t_sync && type & t_brodcaster){
stream << playdata.seek;
}
if(type & TypePackage::t_song_h && type & t_brodcaster){
stream << header;
}
if(type & TypePackage::t_song && type & t_brodcaster){
stream << source;
}
}
return temp;
}
bool package::parseFrom(QByteArray &array){
type = TypePackage::t_void;
QDataStream stream(&array, QIODevice::ReadOnly);
unsigned char temp_type;
stream >> temp_type;
type = static_cast<TypePackage> (temp_type);
if(type & TypePackage::t_sync){
stream >> playdata.seek;
}
if(type & TypePackage::t_song_h){
stream >> header;
}
if(type & TypePackage::t_song){
stream >> source;
}
return isValid();
}
package::~package(){}
Node::Node(const QString &addres, int port):QTcpServer(){ Node::Node(const QString &addres, int port):QTcpServer(){
QString address = addres; QString address = addres;
fBroadcaster = false;
if(address == DEFAULT_ADRESS){ if(address == DEFAULT_ADRESS){
address = LocalScanner::thisAddress().toString(); address = LocalScanner::thisAddress().toString();
} }
@ -132,7 +19,9 @@ Node::Node(const QString &addres, int port):QTcpServer(){
#ifdef QT_DEBUG #ifdef QT_DEBUG
qDebug() << "node started on:" << serverAddress().toString() << "port:" << serverPort(); qDebug() << "node started on:" << serverAddress().toString() << "port:" << serverPort();
#endif #endif
connect(this,SIGNAL(newConnection()),SLOT(newConnection_())); connect(this,SIGNAL(newConnection()),SLOT(newConnection_()));
} }
void Node::acceptError_(ETcpSocket*c){ void Node::acceptError_(ETcpSocket*c){
@ -145,6 +34,14 @@ void Node::acceptError_(ETcpSocket*c){
delete c; delete c;
} }
bool Node::isBroadcaster()const{
return fBroadcaster;
}
void Node::setBroadcaster(bool newValue){
fBroadcaster = newValue;
}
QList<ETcpSocket*>* Node::getClients(){ QList<ETcpSocket*>* Node::getClients(){
return &clients; return &clients;
} }
@ -152,11 +49,18 @@ QList<ETcpSocket*>* Node::getClients(){
void Node::newConnection_(){ void Node::newConnection_(){
ETcpSocket *newClient=new ETcpSocket(nextPendingConnection()); ETcpSocket *newClient=new ETcpSocket(nextPendingConnection());
clients.push_back(newClient); clients.push_back(newClient);
connect(newClient,SIGNAL(Disconnected(ETcpSocket*)),this,SLOT(acceptError_(ETcpSocket*))); connect(newClient, SIGNAL(Disconnected(ETcpSocket*)),
connect(newClient,SIGNAL(Message(ETcpSocket*)),this,SLOT(readData(ETcpSocket*))); this, SLOT(acceptError_(ETcpSocket*)));
connect(newClient, SIGNAL(Message(ETcpSocket*)), this, SLOT(readData(ETcpSocket*)));
connect(newClient, SIGNAL(synced()), this, SLOT(synced()));
emit ClientConnected(newClient); emit ClientConnected(newClient);
} }
void Node::synced(){
emit NodeSynced(static_cast<ETcpSocket*>(this->sender()));
}
void Node::readData(ETcpSocket *c){ void Node::readData(ETcpSocket *c){
emit Message(c); emit Message(c);
} }
@ -211,6 +115,5 @@ Node::~Node(){
this->close(); this->close();
} }
}

View File

@ -2,104 +2,101 @@
#define NODE_H #define NODE_H
#include <QTcpServer> #include <QTcpServer>
#include "ETcpSocket.h" #include "ETcpSocket.h"
#include "song.h" #include <QTimer>
#include "config.h" #include "basepackage.h"
#include "syncpackage.h"
namespace syncLib {
typedef unsigned char Type;
/** /**
* @brief The TypePackage enum * @brief The Node class is tcp server class
* t_void = this package empty and not valid.
* t_play = play current audio file.
* t_song_h = the header of playing audio file.
* t_song = the package with this type is necessary for translite media data on network.
* t_sync = the infomation about sync playning media file on network.
* t_close = the information about close channel.
* t_stop = the package with type 'stop' necessary for stoping playning media files.
* t_what = request for information about the node
* t_brodcaster = information about the node
*/
enum TypePackage{
t_void = 0x00,
t_play = 0x01,
t_song_h = 0x02,
t_song = 0x04,
t_sync = 0x08,
t_close = 0x10,
t_stop = 0x20,
t_what = 0x40,
t_brodcaster = 0x80
};
/**
* @brief The package class. Package for translite media data on network
*
* parse map:
* 1 byle - type
* data
*/ */
class package
{
private:
Type type;
Song source;
SongHeader header;
Syncer playdata;
public:
package();
package(QByteArray &array);
~package();
/**
* @brief getHeader
* @return Header of the song
*/
const SongHeader& getHeader() const;
/**
* @brief getSong
* @return Song
*/
const Song& getSong() const;
/**
* @brief getPlayTime
* @return time of playning media data
*/
const Syncer &getPlayData() const;
const Type& getType() const;
bool isValid() const;
void clear();
QByteArray parseTo();
bool parseFrom(QByteArray& array);
friend class Sync;
};
class Node:public QTcpServer{ class Node:public QTcpServer{
Q_OBJECT Q_OBJECT
private:
QTimer *timer;
int index;
protected: protected:
QList<ETcpSocket*> clients; QList<ETcpSocket*> clients;
bool fBroadcaster;
int step;
private slots: private slots:
void synced();
void acceptError_(ETcpSocket*); void acceptError_(ETcpSocket*);
void newConnection_(); void newConnection_();
void readData(ETcpSocket*_client); void readData(ETcpSocket*_client);
public: public:
Node(const QString &addres = DEFAULT_ADRESS, int port = DEFAULT_PORT); Node(const QString &addres = DEFAULT_ADRESS, int port = DEFAULT_PORT);
/**
* @brief isBroadcaster
* @return true if this node is server
*/
bool isBroadcaster()const;
/**
* @brief setBroadcaster set new state for this node
*/
void setBroadcaster(bool newValue);
/**
* @brief WriteAll send package to all connected clients
*/
void WriteAll(const QByteArray&); void WriteAll(const QByteArray&);
/**
* @brief disconnectClient disconet a client
*/
void disconnectClient(ETcpSocket*); void disconnectClient(ETcpSocket*);
/**
* @brief getClients
* @return list of all connected clients
*/
QList<ETcpSocket*>* getClients(); QList<ETcpSocket*>* getClients();
/**
* @brief addNode add new client for network
* @param node if of node
* @param port port of node
* @return true if all done
*/
bool addNode(const QString &node, int port); bool addNode(const QString &node, int port);
/**
* @brief addNode a connected node
* @param node tcp socket
* @return true if all done
*/
bool addNode(ETcpSocket* node); bool addNode(ETcpSocket* node);
~Node(); ~Node();
signals: signals:
/**
* @brief Error signal when a error detected
*/
void Error(QString); void Error(QString);
/**
* @brief Message signal when accepted a mewssage from other node
*/
void Message(ETcpSocket*); void Message(ETcpSocket*);
/**
* @brief ClientDisconnected - signal when node disconected from this node
*/
void ClientDisconnected(ETcpSocket*); void ClientDisconnected(ETcpSocket*);
/**
* @brief ClientConnected signal when connected a new node
*/
void ClientConnected(ETcpSocket*); void ClientConnected(ETcpSocket*);
/**
* @brief NodeSynced emited when socket synced
*/
void NodeSynced(ETcpSocket*);
}; };
}
#endif // NODE_H #endif // NODE_H

View File

@ -3,8 +3,6 @@
#include <QMediaPlaylist> #include <QMediaPlaylist>
#include "song.h" #include "song.h"
using namespace syncLib;
/** /**
* @brief The PlayList class * @brief The PlayList class
* palyList with songs info * palyList with songs info

View File

@ -3,8 +3,6 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QFile> #include <QFile>
namespace syncLib{
static const QStringList ValidSongs = {".mp3", ".wav", ".ogg"}; static const QStringList ValidSongs = {".mp3", ".wav", ".ogg"};
SongHeader::SongHeader() SongHeader::SongHeader()
{ {
@ -161,8 +159,8 @@ QMediaContent SongStorage::toMedia()const{
return QMediaContent(url); return QMediaContent(url);
} }
bool SongStorage::toSong(Song&)const{ bool SongStorage::toSong(Song& song)const{
Song song(*((SongHeader*)this)); song = (*((SongHeader*)this));
QFile f(url.toLocalFile()); QFile f(url.toLocalFile());
@ -230,4 +228,3 @@ QDataStream& operator >> (QDataStream& stream, Song& song){
return stream; return stream;
} }
}

View File

@ -6,8 +6,6 @@
#include "chronotime.h" #include "chronotime.h"
#include <QMediaContent> #include <QMediaContent>
namespace syncLib {
/** /**
* @brief The Syncer struct * @brief The Syncer struct
* *
@ -18,6 +16,11 @@ struct Syncer
* @brief seek - wher is play media file * @brief seek - wher is play media file
*/ */
milliseconds seek; milliseconds seek;
/**
* @brief timeOn - when play this media file
*/
milliseconds timeOn;
}; };
/** /**
@ -97,5 +100,4 @@ public:
friend class MySql; friend class MySql;
}; };
}
#endif // SONG_H #endif // SONG_H

View File

@ -10,8 +10,6 @@
#include <QDebug> #include <QDebug>
#endif #endif
namespace syncLib{
Sync::Sync(const QString &address, int port, const QString &datadir): Sync::Sync(const QString &address, int port, const QString &datadir):
node(nullptr), node(nullptr),
player(nullptr) player(nullptr)
@ -26,27 +24,25 @@ Sync::Sync(const QString &address, int port, const QString &datadir):
player->setPlaylist(playList->getList()); player->setPlaylist(playList->getList());
fbroadcaster = false;
resyncCount = 0;
lastSyncTime = 0;
ping = 0;
sql = new MySql(datadir); sql = new MySql(datadir);
connect(node, SIGNAL(Message(ETcpSocket*)), SLOT(packageRender(ETcpSocket*))); connect(node, SIGNAL(Message(ETcpSocket*)), SLOT(packageRender(ETcpSocket*)));
connect(&deepScaner, SIGNAL(scaned(QList<ETcpSocket*>*)), SLOT(deepScaned(QList<ETcpSocket*>*))); connect(&deepScaner, SIGNAL(scaned(QList<ETcpSocket*>*)), SLOT(deepScaned(QList<ETcpSocket*>*)));
connect(player, SIGNAL(positionChanged(qint64)), SIGNAL(seekChanged(qint64))); connect(player, SIGNAL(positionChanged(qint64)), SIGNAL(seekChanged(qint64)));
connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(endPlay(QMediaPlayer::State))); connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(endPlay(QMediaPlayer::State)));
connect(node, SIGNAL(NodeSynced(ETcpSocket*)), SLOT(clientSynced(ETcpSocket*)));
} }
MySql* Sync::getSqlApi(){ MySql* Sync::getSqlApi(){
return sql; return sql;
} }
bool Sync::setSingle(const QMediaContent& media){ bool Sync::setSingle(const SongStorage& media){
playList->clear(); playList->clear();
playList->addMedia(media); if(!playList->addMedia(media)) {
return false;
}
emit currentPlayListChanged(); emit currentPlayListChanged();
return true; return true;
@ -71,9 +67,9 @@ const QString& Sync::getPlayListName() const{
} }
bool Sync::play(bool fbroadcast){ bool Sync::play(bool fbroadcast){
fbroadcaster = fbroadcast; node->setBroadcaster(fbroadcast);
if(fbroadcaster){ if(fbroadcast){
player->play(); player->play();
sync(); sync();
}else{ }else{
@ -95,7 +91,9 @@ bool Sync::play(const SongStorage &song, bool fbroadcast){
} }
playList->clear(); playList->clear();
playList->addMedia(song); if(!playList->addMedia(song)){
return false;
}
return play(fbroadcast); return play(fbroadcast);
} }
@ -126,9 +124,9 @@ bool Sync::play(const Song &song, bool fbroadcast){
return false; return false;
} }
QMediaContent savedSong; SongStorage savedSong;
if(!sql->find(static_cast<const SongHeader&>(song), savedSong) && sql->save(song) > -1 && if(!sql->find(static_cast<const SongHeader&>(song), savedSong) && sql->save(song) > -1 &&
!sql->find((SongHeader&)song, savedSong)){ !sql->find(static_cast<const SongHeader&>(song), savedSong)){
return false; return false;
} }
@ -136,19 +134,6 @@ bool Sync::play(const Song &song, bool fbroadcast){
return play(savedSong, fbroadcast); return play(savedSong, fbroadcast);
} }
bool Sync::play(const QMediaContent& media, bool fbroadcast){
if(media.isNull()){
return false;
}
if(!setSingle(media)){
return false;
}
return Sync::play(fbroadcast);
}
bool Sync::play(int id_song, bool fbroadcast){ bool Sync::play(int id_song, bool fbroadcast){
if(id_song < 0){ if(id_song < 0){
@ -209,40 +194,62 @@ void Sync::stop(){
void Sync::jump(const qint64 seek){ void Sync::jump(const qint64 seek){
player->setPosition(seek); player->setPosition(seek);
sync();
} }
bool Sync::isReadyToSync()const{ bool Sync::isReadyToSync()const{
return !fbroadcaster && player->isSeekable() return !node->isBroadcaster() && player->isSeekable()
&& (player->state() == QMediaPlayer::PlayingState); && (player->state() == QMediaPlayer::PlayingState);
} }
bool Sync::sync(const Syncer &sync, milliseconds ping){ bool Sync::sync(const Syncer &sync){
if(!isReadyToSync()){ milliseconds now = sync.timeOn - ChronoTime::now();
if(!isReadyToSync() || now < 0 || now > SYNC_TIME){
return false; return false;
} }
player->setPosition(sync.seek + ping);
// QTimer::singleShot(now, [=](){
// player->setPosition(sync.seek);
// player->syncEnd();
// } );
while (ChronoTime::now() < sync.timeOn) { }
player->setPosition(sync.seek);
player->syncEnd(); player->syncEnd();
return true; return true;
} }
/**
* @todo thi nead send a hedaer
*/
void Sync::sync(){ void Sync::sync(){
if(fbroadcaster) if(node->isBroadcaster()) {
QTimer::singleShot(SYNC_TIME, [=]() { for(ETcpSocket *i: *node->getClients()){
sync(i);
}
}
}
void Sync::sync(ETcpSocket* socket){
if(node->isBroadcaster()) {
if(!socket->isSynced()){
socket->sync();
return;
}
package pac; package pac;
if(!createPackage(t_sync, pac)){ if(!createPackage(t_sync, pac, socket->getTime())){
CreatePackageExaption(); CreatePackageExaption();
return; return;
} }
node->WriteAll(pac.parseTo()); node->WriteAll(pac.parseTo());
}); }
} }
bool Sync::addNode(const QString ip, int port){ bool Sync::addNode(const QString ip, int port){
@ -267,6 +274,10 @@ bool Sync::listen(ETcpSocket *server){
return false; return false;
} }
if(!server->isValid()){
return false;
}
if(!server->getSource()->isOpen() && server->getSource()->open(QIODevice::ReadWrite)){ if(!server->getSource()->isOpen() && server->getSource()->open(QIODevice::ReadWrite)){
return false; return false;
} }
@ -279,28 +290,28 @@ bool Sync::listen(ETcpSocket *server){
return server->Write(pac.parseTo()); return server->Write(pac.parseTo());
} }
bool Sync::createPackage(Type type, package &pac){ bool Sync::createPackage(Type type, package &pac, milliseconds time){
pac.clear(); pac.clear();
pac.type = type; pac.type = type;
if(type & TypePackage::t_sync){ bool isbroadcaster = node->isBroadcaster();
if(fbroadcaster)
pac.playdata.seek = player->position(); if(type & TypePackage::t_sync && isbroadcaster){
else pac.playdata.seek = player->position() + SYNC_TIME;
lastSyncTime = ChronoTime::now(); pac.playdata.timeOn = ChronoTime::now(time) + SYNC_TIME;
} }
if(type & TypePackage::t_song_h && fbroadcaster){ if(type & TypePackage::t_song_h && isbroadcaster ){
if(playList->getList()->currentIndex() < 0) if(playList->getList()->currentIndex() < 0)
return false; return false;
pac.header = *playList->currentHeader(); pac.header = *playList->currentHeader();
} }
if(type & TypePackage::t_song && fbroadcaster){ if(type & TypePackage::t_song && isbroadcaster){
if(playList->getList()->currentIndex() < 0) if(playList->getList()->currentIndex() < 0)
return false; return false;
@ -309,7 +320,7 @@ bool Sync::createPackage(Type type, package &pac){
} }
if(fbroadcaster) if(isbroadcaster)
pac.type = TypePackage(pac.type | t_brodcaster); pac.type = TypePackage(pac.type | t_brodcaster);
return pac.isValid(); return pac.isValid();
@ -321,7 +332,6 @@ void Sync::packageRender(ETcpSocket *socket){
while((array = socket->topStack())){ while((array = socket->topStack())){
package pkg; package pkg;
if(!pkg.parseFrom((*array))){ if(!pkg.parseFrom((*array))){
throw BadAnswerExaption();
socket->nextItem(); socket->nextItem();
continue; continue;
} }
@ -339,44 +349,22 @@ void Sync::packageRender(ETcpSocket *socket){
emit networkStateChange(); emit networkStateChange();
} }
if(pkg.getType() & t_brodcaster){ if(pkg.getType() & t_brodcaster && !node->isBroadcaster()){
// if requst from server // if requst from server
// calc ping for sync if(pkg.getType() & t_pause){
bool fFromRequst = false; pause(true);
if(lastSyncTime){
ping = ChronoTime::now() - lastSyncTime;
lastSyncTime = 0;
fFromRequst = true;
} }
if(pkg.getType() & t_sync && if(pkg.getType() & t_sync && !sync(pkg.getPlayData())){
!sync(pkg.getPlayData(), (fFromRequst)? ping: ping/2)){
QTimer::singleShot(RESYNC_TIME, [=]() { package answer;
package pac; if(!createPackage(t_sync, answer)){
CreatePackageExaption();
if(resyncCount < MAX_RESYNC_COUNT){ socket->nextItem();
if(!createPackage(t_sync, pac)){
throw CreatePackageExaption();
return;
} }
resyncCount++; socket->Write(answer.parseTo());
}else{
resyncCount = 0;
throw SyncCountError();
return;
}
node->WriteAll(pac.parseTo());
});
}
else if (pkg.getType() & t_sync){
resyncCount = 0;
} }
if(pkg.getType() & t_play && !play(pkg.getHeader(), false) && !play(pkg.getSong(), false)){ if(pkg.getType() & t_play && !play(pkg.getHeader(), false) && !play(pkg.getSong(), false)){
@ -388,7 +376,6 @@ void Sync::packageRender(ETcpSocket *socket){
package answer; package answer;
if(!createPackage(requestType | t_play, answer)){ if(!createPackage(requestType | t_play, answer)){
throw CreatePackageExaption();
socket->nextItem(); socket->nextItem();
continue; continue;
} }
@ -398,18 +385,17 @@ void Sync::packageRender(ETcpSocket *socket){
package answer; package answer;
if(!createPackage(t_sync, answer)){ if(!createPackage(t_sync, answer)){
throw CreatePackageExaption();
socket->nextItem(); socket->nextItem();
continue; continue;
} }
socket->Write(answer.parseTo()); socket->Write(answer.parseTo());
} }
if(pkg.getType() & t_close){ if(pkg.getType() & t_close){
socket->getSource()->close(); socket->getSource()->close();
node->getClients()->removeOne(socket); node->getClients()->removeOne(socket);
servers.removeOne(socket);
emit networkStateChange();
delete socket; delete socket;
return; return;
} }
@ -417,37 +403,34 @@ void Sync::packageRender(ETcpSocket *socket){
if(pkg.getType() & t_what){ if(pkg.getType() & t_what){
package answer; package answer;
if(!createPackage(t_void, answer)){ if(!createPackage(t_void, answer)){
throw CreatePackageExaption();
socket->nextItem(); socket->nextItem();
continue; continue;
} }
socket->Write(answer.parseTo()); socket->Write(answer.parseTo());
} }
}else{ } else if (node->isBroadcaster()) {
// if requst from client
// if requst from client if(pkg.getType() & t_sync){
if(pkg.getType() & t_play & t_sync){ if(socket->isSynced()){
if(playList->getList()->currentIndex() < 0){ sync(socket);
throw SyncError(); } else {
socket->sync();
}
socket->nextItem(); socket->nextItem();
continue; continue;
} }
}
package answer; package answer;
if(!createPackage(pkg.getType() & ~t_what & ~t_stop & ~t_brodcaster, answer)){ if(createPackage(pkg.getType() & ~t_sync & ~t_pause & ~t_what & ~t_close & ~t_brodcaster, answer)){
throw CreatePackageExaption();
socket->nextItem();
continue;
}
socket->Write(answer.parseTo()); socket->Write(answer.parseTo());
}
if(pkg.getType() & t_close){ if(pkg.getType() & t_close){
socket->getSource()->close(); socket->getSource()->close();
node->getClients()->removeOne(socket); node->getClients()->removeOne(socket);
delete socket; delete socket;
return;
} }
} }
@ -459,7 +442,6 @@ void Sync::packageRender(ETcpSocket *socket){
void Sync::rescan(bool deep){ void Sync::rescan(bool deep){
package pac; package pac;
if(!createPackage(t_what, pac)){ if(!createPackage(t_what, pac)){
throw CreatePackageExaption();
return; return;
} }
node->WriteAll(pac.parseTo()); node->WriteAll(pac.parseTo());
@ -473,7 +455,6 @@ void Sync::rescan(bool deep){
void Sync::deepScaned(QList<ETcpSocket *> * list){ void Sync::deepScaned(QList<ETcpSocket *> * list){
package pac; package pac;
if(!createPackage(t_what, pac)){ if(!createPackage(t_what, pac)){
throw CreatePackageExaption();
return; return;
} }
QByteArray array = pac.parseTo(); QByteArray array = pac.parseTo();
@ -487,7 +468,7 @@ void Sync::endPlay(QMediaPlayer::State state){
switch (state) { switch (state) {
case QMediaPlayer::StoppedState: case QMediaPlayer::StoppedState:
fbroadcaster = false; node->setBroadcaster(false);
break; break;
case QMediaPlayer::PlayingState: case QMediaPlayer::PlayingState:
sync(); sync();
@ -495,8 +476,6 @@ void Sync::endPlay(QMediaPlayer::State state){
case QMediaPlayer::PausedState: case QMediaPlayer::PausedState:
break; break;
default:
break;
} }
emit playStateChanged(); emit playStateChanged();
@ -511,17 +490,17 @@ bool Sync::setValume(unsigned int valume){
if(valume > 100 || !player->isSynced()) if(valume > 100 || !player->isSynced())
return false; return false;
player->setVolume(valume); player->setVolume(static_cast<int>(valume));
return true; return true;
} }
unsigned int Sync::getValume() const{ unsigned int Sync::getValume() const{
return player->volume(); return static_cast<unsigned int>(player->volume());
} }
unsigned int Sync::seek() const{ unsigned int Sync::seek() const{
return player->position(); return static_cast<unsigned int>(player->position());
} }
const QList<SongStorage>* Sync::getPlayList() const{ const QList<SongStorage>* Sync::getPlayList() const{
@ -554,8 +533,8 @@ bool Sync::updatePlayList(const QString &_playList){
if(!playList->size()) if(!playList->size())
return false; return false;
if(fbroadcaster){ if(node->isBroadcaster()){
play(fbroadcaster); play(true);
} }
return true; return true;
@ -584,6 +563,10 @@ QMediaPlayer::State Sync::playState()const{
return player->state(); return player->state();
} }
void Sync::clientSynced(ETcpSocket* socket){
sync(socket);
}
Sync::~Sync(){ Sync::~Sync(){
delete node; delete node;
delete player; delete player;
@ -593,6 +576,5 @@ Sync::~Sync(){
} }
}

View File

@ -10,8 +10,6 @@
#include "player.h" #include "player.h"
#include "playlist.h" #include "playlist.h"
namespace syncLib {
typedef std::chrono::time_point<std::chrono::high_resolution_clock> Clock; typedef std::chrono::time_point<std::chrono::high_resolution_clock> Clock;
class Node; class Node;
@ -30,10 +28,6 @@ private:
PlayList *playList; PlayList *playList;
QString lastUsedPlayList; QString lastUsedPlayList;
QList<ETcpSocket*> servers; QList<ETcpSocket*> servers;
bool fbroadcaster;
int resyncCount;
int lastSyncTime;
int ping;
LocalScanner deepScaner; LocalScanner deepScaner;
MySql *sql; MySql *sql;
int port; int port;
@ -50,15 +44,17 @@ private:
* @param pac - the resulting value * @param pac - the resulting value
* @return true if everything's done * @return true if everything's done
*/ */
bool createPackage(Type type , package& pac); bool createPackage(Type type , package& pac, milliseconds time = 0);
private slots: private slots:
void clientSynced(ETcpSocket*);
/** /**
* @brief setSingle set singl or temp playlist * @brief setSingle set singl or temp playlist
* @return true if all done * @return true if all done
*/ */
bool setSingle(const QMediaContent& media); bool setSingle(const SongStorage &media);
/** /**
* @brief updateSongs use method update avelable songs from sql database * @brief updateSongs use method update avelable songs from sql database
@ -115,14 +111,6 @@ public:
*/ */
bool play(bool fbroadcast = true); bool play(bool fbroadcast = true);
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song
* @param fbroadcast - server broadcasting sound.
* @return true if all done else false.
*/
bool play(const QMediaContent &media, bool fbroadcast = true);
/** /**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption. * @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song * @param header of song
@ -179,7 +167,7 @@ public:
* @brief sync with server * @brief sync with server
* @param sync - data of sync * @param sync - data of sync
*/ */
bool sync(const Syncer& sync, milliseconds ping); bool sync(const Syncer& sync);
/** /**
* @brief isReadyToSync * @brief isReadyToSync
@ -192,6 +180,11 @@ public:
*/ */
void sync(); void sync();
/**
* @brief sync with clients
*/
void sync(ETcpSocket *socket);
/** /**
* @brief addNode add new connect * @brief addNode add new connect
* @param ip of connection * @param ip of connection
@ -345,7 +338,6 @@ signals:
void playStateChanged(); void playStateChanged();
}; };
}
#endif // SYNC_H #endif // SYNC_H

113
sync/syncpackage.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "syncpackage.h"
#include <QDataStream>
#include <QDebug>
SyncPackage::SyncPackage()
{
type = TypeSyncPackage::t_voidSync;
firstByte = sourceBytes = 0;
}
bool SyncPackage::isValid() const{
switch (type) {
case TypeSyncPackage::t_voidSync:
return false;
case TypeSyncPackage::t_Header:
return firstByte >= 0;
default:
return firstByte >= 0 && sourceBytes != 0;
}
}
void SyncPackage::clear(){
type = TypeSyncPackage::t_voidSync;
firstByte = 0;
sourceBytes = 0;
}
QByteArray SyncPackage::parseTo(){
QByteArray temp;
QDataStream stream(&temp, QIODevice::WriteOnly);
temp.clear();
if(isValid()){
stream << static_cast<unsigned char>(type);
switch (type) {
case TypeSyncPackage::t_End:
stream << firstByte;
stream << sourceBytes;
stream << nativeTime;
break;
default:
stream << firstByte;
stream << sourceBytes;
}
} else {
qDebug() << "package synk is not valid!!!";
}
return temp;
}
bool SyncPackage::parseFrom(QByteArray &array){
type = TypeSyncPackage::t_voidSync;
QDataStream stream(&array, QIODevice::ReadOnly);
unsigned char temp_type;
stream >> temp_type;
type = static_cast<TypeSyncPackage> (temp_type);
switch (type) {
case TypeSyncPackage::t_End:
stream >> firstByte;
stream >> sourceBytes;
stream >> nativeTime;
break;
default:
stream >> firstByte;
stream >> sourceBytes;
}
return isValid();
}
TypeSyncPackage SyncPackage::getType()const {
return type;
}
char SyncPackage::getIndex()const {
return firstByte;
}
const milliseconds& SyncPackage::getNative()const {
return nativeTime;
}
const milliseconds& SyncPackage::getTime()const {
return sourceBytes;
}
const milliseconds& SyncPackage::getPing()const {
return sourceBytes;
}
char SyncPackage::getPrecision()const {
return firstByte;
}
bool SyncPackage::isSended()const {
return type == TypeSyncPackage::t_Responce && firstByte;
}
SyncPackage::~SyncPackage(){}

100
sync/syncpackage.h Normal file
View File

@ -0,0 +1,100 @@
#ifndef SYNCPACKAGE_H
#define SYNCPACKAGE_H
#include "chronotime.h"
#include <QByteArray>
class ETcpSocket;
enum TypeSyncPackage{
t_voidSync = 0x00,
t_Header = 0x01,
t_Responce = 0x02,
t_Source = 0x04,
t_End = 0x08
};
class SyncPackage
{
private:
TypeSyncPackage type;
qint8 firstByte;
milliseconds ping;
milliseconds nativeTime;
milliseconds sourceBytes;
public:
SyncPackage();
/**
* @brief getType
* @return type of package
*/
TypeSyncPackage getType() const;
/**
* @brief getIndex
* @return return index
*/
char getIndex()const;
/**
* @brief getDelay
* @return return delay
*/
const milliseconds& getNative()const;
/**
* @brief getTime
* @return time
*/
const milliseconds& getTime()const;
/**
* @brief getPing
* @return ping
*/
const milliseconds& getPing()const;
/**
* @brief getPrecision
* @return precision
*/
char getPrecision()const;
/**
* @brief isSended
* @return true if package sended
*/
bool isSended()const;
/**
* @brief isValid
* @return true if package is valid
*/
bool isValid() const;
/**
* @brief clear all date of package
*/
void clear();
/**
* @brief parseTo parse this package to byte array
* @return byte array
*/
QByteArray parseTo();
/**
* @brief parseFrom create a package from bytes
* @param array of bytes
* @return true if package valid
*/
bool parseFrom(QByteArray& array);
~SyncPackage();
friend class ETcpSocket;
};
#endif // SYNCPACKAGE_H

11
testLocalTimer/main.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

View File

@ -0,0 +1,39 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QLabel>
#include <QDateTime>
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow) {
ui->setupUi(this);
timer.setInterval(0);
color = Qt::white;
connect(&timer, &QTimer::timeout, this, &MainWindow::handleTick);
timer.start();
}
void MainWindow::keyPressEvent(QKeyEvent * key) {
if (key->key() == Qt::Key_Space) {
if (timer.isActive()) {
timer.stop();
} else {
timer.start();
}
}
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::handleTick() {
auto time = QDateTime::currentMSecsSinceEpoch();
ui->msec->setText(QString::number(time));
}

View File

@ -0,0 +1,28 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
void keyPressEvent(QKeyEvent*);
~MainWindow();
private:
QTimer timer;
QColor color;
Ui::MainWindow *ui;
private slots:
void handleTick();
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="msec">
<property name="styleSheet">
<string notr="true">font: 48pt &quot;Ubuntu&quot;;</string>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,38 @@
#-------------------------------------------------
#
# Project created by QtCreator 2018-08-16T20:39:30
#
#-------------------------------------------------
QT += core gui widgets
TARGET = testLocalTimer
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target