Commit 68516f8a authored by Leander Schulten's avatar Leander Schulten
Browse files

Added a sync service.

Added QObjects.
Add a TimeDistortionFilter ans Speed options.
Added connections between Objects, eg when a device is deleted, deviceProgramms of this device are also deleted.
parent 9e83c460
......@@ -17,7 +17,8 @@ SOURCES += main.cpp \
programmprototype.cpp \
programm.cpp \
applicationdata.cpp \
namedobject.cpp
namedobject.cpp \
dmxchannelfilter.cpp
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
......@@ -39,4 +40,6 @@ HEADERS += \
programmprototype.h \
programm.h \
applicationdata.h \
namedobject.h
namedobject.h \
syncservice.h \
dmxchannelfilter.h
#include "channel.h"
Channel::Channel(const QJsonObject &o):NamedObject(o),IDBase<Channel>(o),index(o["index"].toInt()){}
Channel::Channel(const QJsonObject &o):NamedObject(o,&syncServiceClassName),IDBase<Channel>(o),index(o["index"].toInt()){}
void Channel::writeJsonObject(QJsonObject &o) const{
......
......@@ -2,6 +2,7 @@
#define CHANNEL_H
#include <QString>
#include "idbase.h"
#include "namedobject.h"
/**
......@@ -19,7 +20,8 @@ private:
int index;
friend class DevicePrototype;
public:
Channel(int index, QString name = "Unknown", QString description=""):NamedObject(name,description),index(index){}
static QString syncServiceClassName;
Channel(int index, QString name = "Unknown", QString description=""):NamedObject(name,description,&syncClassName),index(index){}
Channel(const QJsonObject &o);
int getIndex()const{return index;}
......
#include "device.h"
Device::Device(const QJsonObject &o):NamedObject(o),IDBase<Device>(o),
Device::Device(const QJsonObject &o):NamedObject(o,&syncServiceClassName),IDBase<Device>(o),
prototype(IDBase<DevicePrototype>::getIDBaseObjectByID(o["prototype"].toString().toLong())),
startDMXChannel(o["startDMXChannel"].toInt()),
position(o["position"].toObject()["x"].toInt(),o["position"].toObject()["y"].toInt()){
......@@ -15,5 +15,19 @@ void Device::writeJsonObject(QJsonObject &o) const{
position.insert("x",this->position.x());
position.insert("y",this->position.y());
o.insert("position",position);
o.insert("startDMXChannel",startDMXChannel);
o.insert("startDMXChannel",static_cast<int>(startDMXChannel));
}
void Device::channelRemoved(Channel *c){
for(auto i = filter.cbegin();i!=filter.cend();++i){
if(i->first==c){
delete i->second;
filter.erase(i);
return;
}
}
}
void Device::channelAdded(Channel *c){
filter.emplace_back(c,new DMXChannelFilter());
}
......@@ -4,6 +4,8 @@
#include "deviceprototype.h"
#include "QPoint"
#include "namedobject.h"
#include "dmxchannelfilter.h"
/**
......@@ -13,7 +15,7 @@ class Device : public NamedObject, public IDBase<Device>
{
Q_OBJECT
Q_PROPERTY(DevicePrototype * prototype READ getPrototype CONSTANT)
Q_PROPERTY(int startDMXChannel READ getStartDMXChannel WRITE setStartDMXChannel NOTIFY startDMXChannelChanged)
Q_PROPERTY(unsigned int startDMXChannel READ getStartDMXChannel WRITE setStartDMXChannel NOTIFY startDMXChannelChanged)
Q_PROPERTY(QPoint position READ getPosition WRITE setPosition NOTIFY positionChanged)
public:
/**
......@@ -25,26 +27,59 @@ protected:
/**
* @brief startDMXChannel Der Start DMX Channel
*/
int startDMXChannel;
unsigned int startDMXChannel;
/**
* @brief position Die Position des Geräts, es wird eine Karte geben, wo dann dieses Gerät mit diese Position eingezeichnet wird
*/
QPoint position;
/**
* @brief filter Für jeden Channel einen Filter, der einen Default Value setzt und endergebnisse des Programms filtert;
*/
std::vector<std::pair<Channel*,DMXChannelFilter*>> filter;
private slots:
void channelRemoved(Channel *);
void channelAdded(Channel *);
public:
Device(const QJsonObject &o);
Device(DevicePrototype * prototype, int startDMXChannel, QString name, QString desciption="",QPoint position = QPoint(-1,-1)):NamedObject(name,desciption),prototype(prototype),startDMXChannel(startDMXChannel),position(position){}
Device(DevicePrototype * prototype, int startDMXChannel, QString name, QString desciption="",QPoint position = QPoint(-1,-1)):NamedObject(name,desciption),prototype(prototype),startDMXChannel(startDMXChannel),position(position){
connect(prototype,&DevicePrototype::channelAdded,this,&Device::channelAdded);
}
void writeJsonObject(QJsonObject &o)const;
QPoint getPosition()const{return position;}
Q_SLOT void setPosition(const QPoint &p){if(p == position)return;position = p;emit positionChanged(position);}
Q_SLOT void setPosition(const QPoint &p){if(p == position)return;position = p;emit positionChanged(position);SyncService::addUpdateMessage("Device",getID(),"position",QString::number(position.x())+'x'+QString::number(position.y()));}
unsigned int getStartDMXChannel()const{return startDMXChannel;}
Q_SLOT void setStartDMXChannel(unsigned int newStart){if(newStart == startDMXChannel)return;startDMXChannel = newStart; emit startDMXChannelChanged(startDMXChannel);SyncService::addUpdateMessage("Device",getID(),"startDMXChannel",QString::number(startDMXChannel));}
const std::vector<Channel*> & getChannels()const{return prototype->getChannels();}
const std::vector<std::pair<Channel*,DMXChannelFilter*>> & getChannelFilter()const{return filter;}
int getStartDMXChannel()const{return startDMXChannel;}
Q_SLOT void setStartDMXChannel(int newStart){if(newStart == startDMXChannel)return;startDMXChannel = newStart; emit startDMXChannelChanged(startDMXChannel);}
static void update (const ID &id, const QString &name,const QString &value){
auto d = IDBase<Device>::getIDBaseObjectByID(id);
if(d){
if(name == "position"){
const auto values = value.split('x');
d->setPosition(QPoint(values.at(0).toInt(),values.at(1).toInt()));
}else{
const auto data = name.toLatin1();
d->setProperty(data.data(),value);
}
}
}
static void create (const QJsonObject &o){new Device(o);}
static void remove (const ID &id){
auto d = IDBase<Device>::getIDBaseObjectByID(id);
if(d){
delete d;
}
}
signals:
void startDMXChannelChanged(int newStartDMXChannel);
void startDMXChannelChanged(unsigned int newStartDMXChannel);
void positionChanged(const QPoint & newPosition);
};
......
......@@ -7,6 +7,8 @@ void DevicePrototype::removeChannels(int newMaxIndex){
}
// Elemente ganz hinten löschen
for (int i = 0; i < getNumberOfChannels() - newMaxIndex; ++i) {
channelRemoved(channels.back());
SyncService::addRemoveMemberMessage("DevicePrototype",getID(),"channels",channels.back()->getID());
delete channels.back();
channels.pop_back();
}
......@@ -15,13 +17,23 @@ void DevicePrototype::removeChannels(int newMaxIndex){
void DevicePrototype::addChannel(int channel, QString name, QString description){
// dummy Channels einfügen
if (channel>=getNumberOfChannels()) {
for (int i = getNumberOfChannels(); i <= channel; ++i) {
for (int i = getNumberOfChannels(); i < channel; ++i) {
channels.push_back(new Channel(i));
}
emit channelAdded(channels.back());
QJsonObject o;
channels.back()->writeJsonObject(o);
SyncService::addCreateMemberMessage("DevicePrototype",getID(),"channels",o);
}
channels.push_back(new Channel(channel,name,description));
emit channelAdded(channels.back());
QJsonObject o;
channels.back()->writeJsonObject(o);
SyncService::addCreateMemberMessage("DevicePrototype",getID(),"channels",o);
}else{
// eigenschaften setzten
channels[channel]->setName(name);
channels[channel]->setDescription(description);
}
// eigenschaften setzten
channels[channel]->setName(name);
channels[channel]->setDescription(description);
}
const Channel * DevicePrototype::getChannelById(const int id) const{
......@@ -49,7 +61,7 @@ const Channel * DevicePrototype::getChannelByIndex(const unsigned int channelInd
return channels[channelIndex];
}
DevicePrototype::DevicePrototype(const QJsonObject &o):NamedObject(o),IDBase(o){
DevicePrototype::DevicePrototype(const QJsonObject &o):NamedObject(o,&syncServiceClassName),IDBase(o){
auto array = o["channels"].toArray();
for(const auto c : array){
channels.push_back(new Channel(c.toObject()));
......
......@@ -43,8 +43,39 @@ public:
const std::vector<Channel*> & getChannels()const{return channels;};
void writeJsonObject(QJsonObject &o)const;
public:
static void update (const ID &id, const QString &name,const QString &value){
auto d = IDBase<DevicePrototype>::getIDBaseObjectByID(id);
if(d){
auto s = name.toLatin1();
d->setProperty(s.data(),QVariant(value));
}
}
static void create (const QJsonObject &o){new DevicePrototype(o);}
static void remove (const ID &id){
auto d = IDBase<DevicePrototype>::getIDBaseObjectByID(id);
if(d){
delete d;
}
}
static void createMember (const ID &id,const QString &name,const QJsonObject &o){
auto d = IDBase<DevicePrototype>::getIDBaseObjectByID(id);
if(d&&name=="channels")d->addChannel(o["index"].toInt(),o["name"].toString(),o["description"].toString());
}
static void removeMember (const ID &pid,const QString &name,const ID &id){
auto d = IDBase<DevicePrototype>::getIDBaseObjectByID(pid);
if(d&&name=="channels"){
auto c = IDBase<Channel>::getIDBaseObjectByID(id);
if(c){
d->removeChannels(c->getIndex());
}
}
}
signals:
void numberOfChannelsChanged();
void channelAdded(Channel*);
void channelRemoved(Channel*);
};
......
#include "dmxchannelfilter.h"
#include "programm.h"
#include "device.h"
void DMXChannelFilter::filterValue(unsigned char *value){
if (shouldOverrideValue_) {
*value = this->value;
}else{
if (minValue!=0&&maxValue!=255) {
if(minOperation==REMAP&&maxOperation==REMAP){
*value = (unsigned char)(*value * (maxValue-minValue)/255.f) + minValue;
}else if(minOperation==CUT&&maxOperation==REMAP){
*value = (unsigned char)(*value * maxValue/255.f);
if(*value<minValue)
*value = minValue;
}else if(minOperation==REMAP&&maxOperation==CUT){
*value = (unsigned char)(*value * (255-minValue)/255.f) + minValue;
if(*value>maxValue)
*value = maxValue;
}else /*if(minOperation==CUT&&maxOperation==CUT)*/{
if(*value<minValue)
*value = minValue;
if(*value>maxValue)
*value = maxValue;
}
}else if(minValue!=0){
if (minOperation==CUT) {
if(*value<minValue)
*value = minValue;
}else{ //REMAP
*value = (unsigned char)(*value * (255-minValue)/255.f) + minValue;
}
}else if(maxValue!=255){
if (maxOperation==CUT) {
if(*value>maxValue)
*value = maxValue;
}else{ //REMAP
*value = (unsigned char)(*value * maxValue/255.f);
}
}
}
}
void DMXChannelFilter::initValue(unsigned char *value){
*value = this->value;
}
void DMXChannelFilter::initValues(unsigned char *values, unsigned int numberOfChannels){
for(const auto d : IDBase<Device>::getAllIDBases()){
for(const auto f : d->getChannelFilter()){
const auto index = d->getStartDMXChannel()+f.first->getIndex();
if(index<numberOfChannels){
f.second->initValue(values+index);
}
}
}
}
void DMXChannelFilter::filterValues(unsigned char *values, unsigned int numberOfChannels){
for(const auto d : IDBase<Device>::getAllIDBases()){
for(const auto f : d->getChannelFilter()){
const auto index = d->getStartDMXChannel()+f.first->getIndex();
if(index<numberOfChannels){
f.second->filterValue(values+index);
}
}
}
}
#ifndef DMXCHANNEL_H
#define DMXCHANNEL_H
#include <QObject>
class DMXChannelFilter : QObject{
Q_OBJECT
public:
enum Operation{CUT, REMAP};
Q_ENUM(Operation)
private:
Operation maxOperation = CUT;
Operation minOperation = CUT;
unsigned char maxValue = 255;
unsigned char minValue = 0;
unsigned char value = 0;
bool shouldOverrideValue_;
public:
Q_SLOT void setMaxOperation(Operation o){if(o==maxOperation)return;maxOperation=o;emit maxOperationChanged(o);}
Operation getMaxOperation()const{return maxOperation;}
Q_SLOT void setMinOperation(Operation o){if(o==minOperation)return;minOperation=o;emit minOperationChanged(o);}
Operation getMinOperation()const{return minOperation;}
Q_SLOT void setMaxValue(unsigned char o){if(o==maxValue)return;maxValue=o;emit maxValueChanged(o);}
unsigned char getMaxValue()const{return maxValue;}
Q_SLOT void setMinValue(unsigned char o){if(o==minValue)return;minValue=o;emit minValueChanged(o);}
unsigned char getMinValue()const{return minValue;}
Q_SLOT void setValue(unsigned char o){if(o==value)return;value=o;emit valueChanged(o);}
unsigned char getValue()const{return value;}
Q_SLOT void shouldOverrideValue(unsigned char o){if(o==shouldOverrideValue_)return;shouldOverrideValue_=o;emit shouldOverrideValueChanged(o);}
unsigned char shouldOverrideValue()const{return shouldOverrideValue_;}
public:
void initValue(unsigned char * value);
void filterValue(unsigned char * value);
public:
static void initValues(unsigned char *values, unsigned int numberOfChannels);
static void filterValues(unsigned char *values, unsigned int numberOfChannels);
signals:
void maxOperationChanged(Operation);
void minOperationChanged(Operation);
void maxValueChanged(unsigned char);
void minValueChanged(unsigned char);
void valueChanged(unsigned char);
void shouldOverrideValueChanged(unsigned char);
};
#endif // DMXCHANNEL_H
\ No newline at end of file
#ifndef NAMEDOBJECT_H
#define NAMEDOBJECT_H
#include "idbase.h"
#include <QObject>
#include <QJsonObject>
#include "syncservice.h"
class NamedObject : public QObject
{
......@@ -11,6 +11,7 @@ class NamedObject : public QObject
Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged)
private:
QString * syncClassName;
/**
* @brief name Der Name des Objects
*/
......@@ -21,12 +22,12 @@ private:
QString description;
public:
//explicit NamedObject(QObject *parent = 0);
NamedObject(QString name, QString description=""):name(name),description(description){}
NamedObject(const QJsonObject &o):name(o["name"].toString()),description(o["description"].toString()){}
NamedObject(QString name, QString description="",QString * syncClassName=nullptr):syncClassName(syncClassName),name(name),description(description){}
NamedObject(const QJsonObject &o,QString * syncClassName=nullptr):syncClassName(syncClassName),name(o["name"].toString()),description(o["description"].toString()){}
Q_SLOT void setName(const QString &n){if(n==name)return;name=n;emit nameChanged(name);}
Q_SLOT void setName(const QString &n){if(n==name)return;name=n;emit nameChanged(name);if(syncClassName)if(!syncClassName->isEmpty())SyncService::addUpdateMessage(*syncClassName,*(ID*)(this+sizeof(this)),"name",name);}
QString getName()const{return name;}
Q_SLOT void setDescription(const QString &d){if(d==description)return;description=d;emit descriptionChanged(description);}
Q_SLOT void setDescription(const QString &d){if(d==description)return;description=d;emit descriptionChanged(description);if(syncClassName)if(!syncClassName->isEmpty())SyncService::addUpdateMessage(*syncClassName,*(ID*)(this+sizeof(this)),"description",description);}
QString getDescription()const{return description;}
void writeJsonObject(QJsonObject &o) const{
......
#include "programm.h"
#include <QJsonArray>
#include <unordered_set>
Programm::Programm(const QJsonObject &o):NamedObject(o),IDBase<Programm>(o)
......@@ -7,38 +8,76 @@ Programm::Programm(const QJsonObject &o):NamedObject(o),IDBase<Programm>(o)
auto array = o["programms"].toArray();
for(const auto r : array){
const auto o = r.toObject();
programms.emplace_back(IDBase<Device>::getIDBaseObjectByID(o["device"].toString().toLong()),
IDBase<ProgrammPrototype>::getIDBaseObjectByID(o["programmPrototype"].toString().toLong()),
o["offset"].toDouble());
addDeviceProgramm(o);
}
}
void Programm::addDeviceProgramm(const QJsonObject &o){
programms.push_back(new DeviceProgramm(IDBase<Device>::getIDBaseObjectByID(o["device"].toString().toLong()),
IDBase<ProgrammPrototype>::getIDBaseObjectByID(o["programmPrototype"].toString().toLong()),
o["offset"].toDouble()));
emit deviceProgrammAdded(programms.back());
connect(programms.back(),&DeviceProgramm::destroyed,this,&Programm::deviceProgrammDeleted);
}
void Programm::writeJsonObject(QJsonObject &o) const{
NamedObject::writeJsonObject(o);
IDBase<Programm>::writeJsonObject(o);
QJsonArray array;
for(const auto dp : programms){
for(auto dp = programms.cbegin();dp!=programms.cend();++dp){
QJsonObject o;
o.insert("offset",dp.offset);
o.insert("device",QString::number(dp.device->getID().value()));
o.insert("programmPrototype",QString::number(dp.getProgrammPrototyp()->getID().value()));
o.insert("offset",(**dp).getOffset());
o.insert("device",QString::number((**dp).device->getID().value()));
o.insert("programmPrototype",QString::number((**dp).getProgrammPrototyp()->getID().value()));
array.append(o);
}
o.insert("programms",array);
}
std::map<Programm*,std::map<Device*,std::vector<Channel*>>> Programm::run(){
if (isRunning()) {
return std::map<Programm*,std::map<Device*,std::vector<Channel*>>>();
}
std::unordered_set<int> newUsedChannels;
for(const auto p : this->programms){
for(const auto cp : p->getProgrammPrototyp()->getChannelProgramms()){
const auto channelNummer = p->device->getStartDMXChannel() + cp->channel->getIndex();
newUsedChannels.insert(channelNummer);
}
}
std::map<Programm*,std::map<Device*,std::vector<Channel*>>> map;
for(const auto p : Programm::getAllIDBases()){
if(p->isRunning()){
//für jedes Device Programm
for(auto dp_ = p->programms.cbegin();dp_!=p->programms.cend();++dp_){
auto dp = *dp_;
//für jedes Channel Programm
for(const auto cp : dp->getProgrammPrototyp()->getChannelProgramms()){
const auto channelNummer = dp->device->getStartDMXChannel() + cp->channel->getIndex();
if(newUsedChannels.find(channelNummer)!=newUsedChannels.end()){
map[p][dp->getDevice()].push_back(cp->getChannel());
}
}
}
}
}
if(map.empty())
setRunning(true);
return map;
}
void Programm::fill(unsigned char *data, size_t length, double time){
// für jedes Programm
for(const auto p : Programm::getAllIDBases()){
if(p->isRunning()){
//für jedes Device Programm
for(const auto dp : p->programms){
for(auto dp_ = p->programms.cbegin();dp_!=p->programms.cend();++dp_){
auto dp = *dp_;
//für jedes Channel Programm
for(const auto cp : dp.getProgrammPrototyp()->getChannelProgramms()){
const auto channelNummer = dp.device->getStartDMXChannel() + cp.channel->getIndex();
for(const auto cp : dp->getProgrammPrototyp()->getChannelProgramms()){
const auto channelNummer = dp->device->getStartDMXChannel() + cp->channel->getIndex();
if(channelNummer<length){
data[channelNummer] = cp.getValueForTime(time + dp.offset);
data[channelNummer] = cp->getValueForTime(p->getTimeDistortion()->distort(time * p->getSpeed()) * dp->getSpeed()+ dp->getOffset());
}
}
}
......@@ -50,11 +89,24 @@ bool Programm::addDeviceProgramm(Device * device, ProgrammPrototype * programmPr
if(device->prototype != programmPrototype->devicePrototype){
return false;
}
for(const auto cp : programms){
if(cp.device == device){
for(auto dp = programms.cbegin();dp!=programms.cend();++dp){
if((**dp).device == device){
return false;
}
}
programms.emplace_back(device, programmPrototype, offset);
auto newDeviceProgramm = new DeviceProgramm(device, programmPrototype, offset);
programms.push_back(newDeviceProgramm);
emit deviceProgrammAdded(newDeviceProgramm);
connect(newDeviceProgramm,&DeviceProgramm::destroyed,this,&Programm::deviceProgrammDeleted);
return true;
}
void Programm::deviceProgrammDeleted(QObject *d){
for(auto i = programms.cbegin();i!=programms.cend();++i){
if(*i == d){
programms.erase(i);
emit deviceProgrammRemoved(qobject_cast<DeviceProgramm*>(d));
return;
}
}
}
......@@ -3,42 +3,153 @@
#include "programmprototype.h"
#include "device.h"
#include "idbase.h"
#include <map>
class DeviceProgramm : public QObject , public IDBase<DeviceProgramm>{
Q_OBJECT
Q_PROPERTY(ProgrammPrototype* programmPrototype READ getProgrammPrototyp WRITE setProgrammPrototype NOTIFY programmPrototypeChanged)
Q_PROPERTY(double offset READ getOffset WRITE setOffset NOTIFY offsetChanged)
Q_PROPERTY(double speed READ getSpeed WRITE setSpeed NOTIFY speedChanged)
Q_PROPERTY(Device* device READ getDevice CONSTANT)
private:
ProgrammPrototype * programmPrototype;
double offset;
double speed = 1.0;
public:
const Device * device;
Device * getDevice()const{return const_cast<Device *>(device);}
Q_SLOT void setOffset(double o){if(o==offset)return;offset=o;emit offsetChanged();SyncService::addUpdateMessage("DeviceProgramm",getID(),"offset",QString::number(offset));}
double getOffset()const{return offset;}
Q_SLOT void setSpeed(double s){if(s==speed)return;speed=s;emit speedChanged(speed);}
double getSpeed()const{return speed;}
DeviceProgramm(Device * device,ProgrammPrototype * programmPrototype,double offset):QObject(device),programmPrototype(programmPrototype),offset(offset),device(device){connect(programmPrototype,&ProgrammPrototype::destroyed,this,&DeviceProgramm::programmPrototypeDeleted);}
bool setProgrammPrototype(ProgrammPrototype * p){
if(p->devicePrototype!=device->prototype){
return false;
}
disconnect(programmPrototype,&ProgrammPrototype::destroyed,this,&DeviceProgramm::programmPrototypeDeleted);
programmPrototype = p;
connect(programmPrototype,&ProgrammPrototype::destroyed,this,&DeviceProgramm::programmPrototypeDeleted);
emit programmPrototypeChanged();
SyncService::addUpdateMessage("DeviceProgramm",getID(),"programmPrototype",QString::number(programmPrototype->getID().value()));
return true;
}
ProgrammPrototype * getProgrammPrototyp()const{return programmPrototype;}
private slots:
void programmPrototypeDeleted(QObject *){delete this;}
public:
static void update (const ID &id, const QString &name,const QString &value){
auto d = IDBase<DeviceProgramm>::getIDBaseObjectByID(id);
if(d){
if (name=="offset") {
d->setOffset(value.toDouble());
}else if(name=="programmPrototype"){
auto proto = IDBase<ProgrammPrototype>::getIDBaseObjectByID(value.toLong());
if(proto) d->setProgrammPrototype(proto);
}
}
}
signals:
void offsetChanged();
void speedChanged(double speed);
void programmPrototypeChanged();
};
class TimeDistortion : QObject{
Q_OBJECT
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(QEasingCurve distortionCurve READ getDistortionCurve WRITE setDistortionCurve NOTIFY distortionCurveChanged)
Q_PROPERTY(double intervall READ getIntervall WRITE setIntervall NOTIFY intervallChanged)
private:
bool enabled;