WIP: attribute support

Dieser Commit ist enthalten in:
reinder 2019-12-21 19:08:30 +01:00
Ursprung 33f4db0d4b
Commit af084bff45
51 geänderte Dateien mit 895 neuen und 153 gelöschten Zeilen

Datei anzeigen

@ -36,6 +36,7 @@
#include "network/object.hpp"
#include "network/property.hpp"
#include "subwindow/hardwarelistsubwindow.hpp"
#include "subwindow/objecteditsubwindow.hpp"
#include "subwindow/serversettingssubwindow.hpp"
#include "subwindow/serverconsolesubwindow.hpp"
@ -47,6 +48,8 @@ MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
m_mdiArea{new QMdiArea()}
{
instance = this;
setWindowTitle("Traintastic");
QAction* actFullScreen;
@ -207,6 +210,21 @@ void MainWindow::toggleFullScreen()
}
}
void MainWindow::showObjectEdit(const QString& id)
{
if(!m_mdiSubWindow.objectEdit.contains(id))
{
ObjectEditSubWindow* window = new ObjectEditSubWindow(id);
m_mdiArea->addSubWindow(window);
window->setAttribute(Qt::WA_DeleteOnClose);
connect(window, &QMdiSubWindow::destroyed, [this, id](QObject*){ m_mdiSubWindow.objectEdit.remove(id); });
window->show();
m_mdiSubWindow.objectEdit[id] = window;
}
else
m_mdiArea->setActiveSubWindow(m_mdiSubWindow.objectEdit[id]);
}
void MainWindow::showHardware()
{
if(!m_mdiSubWindow.hardwareList)

Datei anzeigen

@ -25,10 +25,12 @@
#define MAINWINDOW_HPP
#include <QMainWindow>
#include <QMap>
#include <enum/traintasticmode.hpp>
class QMdiArea;
class QActionGroup;
class ObjectEditSubWindow;
class HardwareListSubWindow;
class ServerSettingsSubWindow;
class ServerConsoleSubWindow;
@ -41,6 +43,7 @@ class MainWindow : public QMainWindow
QMdiArea* m_mdiArea;
struct
{
QMap<QString, ObjectEditSubWindow*> objectEdit;
HardwareListSubWindow* hardwareList = nullptr;
ServerSettingsSubWindow* serverSettings = nullptr;
ServerConsoleSubWindow* serverConsole = nullptr;
@ -73,6 +76,7 @@ class MainWindow : public QMainWindow
void exportWorld();
void toggleFullScreen();
void showHardware();
void showServerSettings();
void showServerConsole();
void showAbout();
@ -80,11 +84,14 @@ class MainWindow : public QMainWindow
void updateModeActions();
public:
inline static MainWindow* instance = nullptr;
MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
public slots:
void connectToServer();
void showObjectEdit(const QString& id);
};
#endif

Datei anzeigen

@ -24,7 +24,7 @@
#define CLIENT_NETWORK_ABSTRACTPROPERTY_HPP
#include "interfaceitem.hpp"
#include <enum/propertytype.hpp>
#include <enum/valuetype.hpp>
class Object;
@ -33,16 +33,16 @@ class AbstractProperty : public InterfaceItem
Q_OBJECT
protected:
const PropertyType m_type;
const ValueType m_type;
public:
explicit AbstractProperty(Object& object, const QString& name, PropertyType type) :
explicit AbstractProperty(Object& object, const QString& name, ValueType type) :
InterfaceItem(object, name),
m_type{type}
{
}
PropertyType type() const { return m_type; }
ValueType type() const { return m_type; }
virtual const QString& enumName() const = 0;

Datei anzeigen

@ -29,7 +29,7 @@
#include "property.hpp"
#include "tablemodel.hpp"
#include <enum/interfaceitemtype.hpp>
#include <enum/propertytype.hpp>
#include <enum/valuetype.hpp>
Client* Client::instance = nullptr;
@ -125,7 +125,7 @@ void Client::setPropertyBool(Property& property, bool value)
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Boolean);
event->write(ValueType::Boolean);
event->write(value);
send(event);
}
@ -135,7 +135,7 @@ void Client::setPropertyInt64(Property& property, int64_t value)
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Integer);
event->write(ValueType::Integer);
event->write(value);
send(event);
}
@ -145,7 +145,7 @@ void Client::setPropertyDouble(Property& property, double value)
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::Float);
event->write(ValueType::Float);
event->write(value);
send(event);
}
@ -155,7 +155,7 @@ void Client::setPropertyString(Property& property, const QString& value)
auto event = Message::newEvent(Message::Command::ObjectSetProperty);
event->write(static_cast<Object*>(property.parent())->m_handle);
event->write(property.name().toLatin1());
event->write(PropertyType::String);
event->write(ValueType::String);
event->write(value.toUtf8());
send(event);
}
@ -229,52 +229,96 @@ ObjectPtr Client::readObject(const Message& message)
while(!message.endOfBlock())
{
message.readBlock(); // item
InterfaceItem* item = nullptr;
const QString name = QString::fromLatin1(message.read<QByteArray>());
const InterfaceItemType type = message.read<InterfaceItemType>();
switch(type)
{
case InterfaceItemType::Property:
{
const PropertyType propertyType = message.read<PropertyType>();
const ValueType type = message.read<ValueType>();
QVariant value;
switch(propertyType)
switch(type)
{
case PropertyType::Boolean:
case ValueType::Boolean:
value = message.read<bool>();
break;
case PropertyType::Enum:
case PropertyType::Integer:
case ValueType::Enum:
case ValueType::Integer:
value = message.read<qint64>();
break;
case PropertyType::Float:
case ValueType::Float:
value = message.read<double>();
break;
case PropertyType::String:
case ValueType::String:
value = QString::fromUtf8(message.read<QByteArray>());
break;
case PropertyType::Object:
case ValueType::Object:
// TODO
break;
case PropertyType::Invalid:
case ValueType::Invalid:
break;
}
// Q_ASSERT(value.isValid());
if(Q_LIKELY(value.isValid()))
{
Property* p = new Property(*obj, name, propertyType, value);
if(propertyType == PropertyType::Enum)
Property* p = new Property(*obj, name, type, value);
if(type == ValueType::Enum)
p->m_enumName = QString::fromLatin1(message.read<QByteArray>());
obj->m_interfaceItems.add(*p);
item = p;
}
break;
}
}
if(Q_LIKELY(item))
{
message.readBlock(); // attributes
while(!message.endOfBlock())
{
message.readBlock(); // item
const AttributeName attributeName = message.read<AttributeName>();
QVariant value;
switch(message.read<ValueType>())
{
case ValueType::Boolean:
value = message.read<bool>();
break;
case ValueType::Enum:
case ValueType::Integer:
value = message.read<qint64>();
break;
case ValueType::Float:
value = message.read<double>();
break;
case ValueType::String:
value = QString::fromUtf8(message.read<QByteArray>());
break;
case ValueType::Object:
case ValueType::Invalid:
Q_ASSERT(false);
break;
}
if(Q_LIKELY(value.isValid()))
item->m_attributes[attributeName] = value;
message.readBlockEnd(); // end attribute
}
message.readBlockEnd(); // end attributes
obj->m_interfaceItems.add(*item);
}
message.readBlockEnd(); // end item
}
message.readBlockEnd(); // end items
@ -329,9 +373,9 @@ void Client::processMessage(const std::shared_ptr<Message> message)
{
if(Property* property = object->getProperty(QString::fromLatin1(message->read<QByteArray>())))
{
switch(message->read<PropertyType>())
switch(message->read<ValueType>())
{
case PropertyType::Boolean:
case ValueType::Boolean:
{
const bool value = message->read<bool>();
property->m_value = value;
@ -339,8 +383,8 @@ void Client::processMessage(const std::shared_ptr<Message> message)
emit property->valueChangedBool(value);
break;
}
case PropertyType::Integer:
case PropertyType::Enum:
case ValueType::Integer:
case ValueType::Enum:
{
const qlonglong value = message->read<qlonglong>();
property->m_value = value;
@ -350,7 +394,7 @@ void Client::processMessage(const std::shared_ptr<Message> message)
emit property->valueChangedInt(static_cast<int>(value));
break;
}
case PropertyType::Float:
case ValueType::Float:
{
const double value = message->read<double>();
property->m_value = value;
@ -358,7 +402,7 @@ void Client::processMessage(const std::shared_ptr<Message> message)
emit property->valueChangedDouble(value);
break;
}
case PropertyType::String:
case ValueType::String:
{
const QString value = QString::fromUtf8(message->read<QByteArray>());
property->m_value = value;
@ -371,6 +415,42 @@ void Client::processMessage(const std::shared_ptr<Message> message)
}
break;
case Message::Command::ObjectAttributeChanged:
if(Object* object = m_objects.value(message->read<Handle>(), nullptr))
{
if(Property* property = object->getProperty(QString::fromLatin1(message->read<QByteArray>())))
{
AttributeName attributeName = message->read<AttributeName>();
QVariant value;
switch(message->read<ValueType>())
{
case ValueType::Boolean:
value = message->read<bool>();
break;
case ValueType::Integer:
case ValueType::Enum:
value = message->read<qlonglong>();
break;
case ValueType::Float:
value = message->read<double>();
break;
case ValueType::String:
value = QString::fromUtf8(message->read<QByteArray>());
break;
}
if(Q_LIKELY(value.isValid()))
{
property->m_attributes[attributeName] = value;
emit property->attributeChanged(attributeName, value);
}
}
}
break;
case Message::Command::TableModelColumnHeadersChanged:
if(TableModel* model = m_tableModels.value(message->read<Handle>(), nullptr))
{

Datei anzeigen

@ -28,3 +28,18 @@ InterfaceItem::InterfaceItem(Object& object, const QString& name) :
m_name{name}
{
}
QVariant InterfaceItem::getAttribute(AttributeName name, const QVariant& default_) const
{
return m_attributes.value(name, default_);
}
bool InterfaceItem::getAttributeBool(AttributeName name, bool default_) const
{
return m_attributes.value(name, default_).toBool();
}
qint64 InterfaceItem::getAttributeInt64(AttributeName name, qint64 default_) const
{
return m_attributes.value(name, default_).toLongLong();
}

Datei anzeigen

@ -24,6 +24,9 @@
#define CLIENT_NETWORK_INTERFACEITEM_HPP
#include <QObject>
#include <QMap>
#include <QVariant>
#include <enum/attributename.hpp>
class Object;
@ -31,14 +34,30 @@ class InterfaceItem : public QObject
{
Q_OBJECT
friend class Client;
protected:
const QString m_name;
QMap<AttributeName, QVariant> m_attributes;
public:
explicit InterfaceItem(Object& object, const QString& name);
const QString& name() const { return m_name; }
const QString& displayName() const { return m_name; }
QVariant getAttribute(AttributeName name, const QVariant& default_) const;
bool getAttributeBool(AttributeName name, bool default_) const;
qint64 getAttributeInt64(AttributeName name, qint64 default_) const;
template<typename T>
T getAttributeEnum(AttributeName name, T default_) const
{
return static_cast<T>(getAttributeInt64(name, static_cast<qint64>(default_)));
}
signals:
void attributeChanged(AttributeName name, const QVariant& value);
};
#endif

Datei anzeigen

@ -23,7 +23,7 @@
#include "property.hpp"
#include "client.hpp"
Property::Property(Object& object, const QString& name, PropertyType type, const QVariant& value) :
Property::Property(Object& object, const QString& name, ValueType type, const QVariant& value) :
AbstractProperty(object, name, type),
m_value{value}
{

Datei anzeigen

@ -40,7 +40,7 @@ class Property : public AbstractProperty
QString m_enumName;
public:
explicit Property(Object& object, const QString& name, PropertyType type, const QVariant& value);
explicit Property(Object& object, const QString& name, ValueType type, const QVariant& value);
const QString& enumName() const final { Q_ASSERT(!m_enumName.isEmpty()); return m_enumName; }

Datei anzeigen

@ -32,7 +32,7 @@ HardwareListSubWindow::HardwareListSubWindow(QWidget* parent) :
QMdiSubWindow(parent)
{
setWindowTitle(tr("Hardware"));
//setWindowIcon(QIcon(":/dark/hardware.svg"));
setWindowIcon(QIcon(":/dark/hardware.svg"));
setWidget(new HardwareWidget(this));
QSettings settings;

Datei anzeigen

@ -0,0 +1,9 @@
#include "objecteditsubwindow.hpp"
#include "../widget/objecteditwidget.hpp"
ObjectEditSubWindow::ObjectEditSubWindow(const QString& id, QWidget* parent) :
QMdiSubWindow(parent)
{
setWindowTitle(id);
setWidget(new ObjectEditWidget(id, this));
}

Datei anzeigen

@ -0,0 +1,12 @@
#ifndef OBJECTEDITSUBWINDOW_HPP
#define OBJECTEDITSUBWINDOW_HPP
#include <QMdiSubWindow>
class ObjectEditSubWindow : public QMdiSubWindow
{
public:
ObjectEditSubWindow(const QString& id, QWidget* parent = nullptr);
};
#endif

Datei anzeigen

@ -35,9 +35,25 @@
#include "../widget/propertylineedit.hpp"
#include "../widget/propertytextedit.hpp"
#include "../widget/propertydirectioncontrol.hpp"
#include <enum/category.hpp>
#include <enum/direction.hpp>
QString toString(Category value)
{
switch(value)
{
case Category::General: return "General";
case Category::Info: return "Info";
case Category::Notes: return "Notes";
case Category::Status: return "Status";
}
return "?";
}
ObjectEditWidget::ObjectEditWidget(const QString& id, QWidget* parent) :
QWidget(parent),
m_id{id}
@ -67,31 +83,34 @@ ObjectEditWidget::~ObjectEditWidget()
void ObjectEditWidget::buildForm()
{
QMap<QString, QWidget*> tabs;
QMap<Category, QWidget*> tabs;
QList<Category> tabOrder;
for(const QString& name : m_object->interfaceItems().names())
if(Property* property = m_object->getProperty(name))
{
Category category = property->getAttributeEnum<Category>(AttributeName::Category, Category::General);
QWidget* w = nullptr;
if(property->type() == PropertyType::Boolean)
if(property->type() == ValueType::Boolean)
w = new PropertyCheckBox(*property);
else if(property->type() == PropertyType::Integer)
else if(property->type() == ValueType::Integer)
w = new PropertySpinBox(*property);
else if(property->type() == PropertyType::String)
else if(property->type() == ValueType::String)
{
if(property->name() == "notes")
if(category == Category::Notes && property->name() == "notes")
{
PropertyTextEdit* edit = new PropertyTextEdit(*property);
edit->setPlaceholderText(property->displayName());
Q_ASSERT(!tabs.contains("notes"));
tabs.insert("notes", edit);
Q_ASSERT(!tabs.contains(category));
tabs.insert(category, edit);
tabOrder.append(category);
continue;
}
else
w = new PropertyLineEdit(*property);
}
else if(property->type() == PropertyType::Enum)
else if(property->type() == ValueType::Enum)
{
if(property->enumName() == EnumName<Direction>::value)
{
@ -103,15 +122,15 @@ void ObjectEditWidget::buildForm()
}
QWidget* tabWidget;
QString tab = "general"; // TODO: get sttribute
if(!tabs.contains(tab))
if(!tabs.contains(category))
{
tabWidget = new QWidget();
tabWidget->setLayout(new QFormLayout());
tabs.insert(tab, tabWidget);
tabs.insert(category, tabWidget);
tabOrder.append(category);
}
else
tabWidget = tabs[tab];
tabWidget = tabs[category];
static_cast<QFormLayout*>(tabWidget->layout())->addRow(property->displayName(), w);
}
@ -119,8 +138,8 @@ void ObjectEditWidget::buildForm()
if(tabs.count() > 1)
{
QTabWidget* tabWidget = new QTabWidget();
for(auto it = tabs.constBegin(); it != tabs.constEnd(); it++)
tabWidget->addTab(it.value(), it.key());
for(Category category : tabOrder)
tabWidget->addTab(tabs.value(category), toString(category));
QVBoxLayout* l = new QVBoxLayout();
l->setMargin(0);
l->addWidget(tabWidget);

Datei anzeigen

@ -33,7 +33,7 @@
#include "../widget/alertwidget.hpp"
#include "objecteditwidget.hpp"
#include "../mainwindow.hpp"
ObjectListWidget::ObjectListWidget(const QString& id, QWidget* parent) :
QWidget(parent),
@ -111,7 +111,7 @@ void ObjectListWidget::tableDoubleClicked(const QModelIndex& index)
{
const QString id = m_tableWidget->getRowObjectId(index.row());
if(!id.isEmpty())
(new ObjectEditWidget(id))->show();
MainWindow::instance->showObjectEdit(id);//emit rowDoubleClicked(id);
}

Datei anzeigen

@ -53,6 +53,9 @@ class ObjectListWidget : public QWidget
public:
explicit ObjectListWidget(const QString& id, QWidget* parent = nullptr);
~ObjectListWidget() override;
//signals:
// void rowDoubleClicked(const QString& id);
};
#endif

Datei anzeigen

@ -23,12 +23,62 @@
#include "propertycheckbox.hpp"
#include "../network/property.hpp"
class InternalUpdateHolder
{
private:
bool& m_value;
public:
inline InternalUpdateHolder(bool& value) :
m_value{value}
{
Q_ASSERT(!m_value);
m_value = true;
}
inline ~InternalUpdateHolder()
{
Q_ASSERT(m_value);
m_value = false;
}
};
PropertyCheckBox::PropertyCheckBox(Property& property, QWidget* parent) :
QCheckBox(parent),
m_property{property}
m_property{property},
m_internalUpdate{false}
{
Q_ASSERT(m_property.type() == PropertyType::Boolean);
Q_ASSERT(m_property.type() == ValueType::Boolean);
setEnabled(m_property.getAttributeBool(AttributeName::Enabled, true));
setVisible(m_property.getAttributeBool(AttributeName::Visible, true));
setChecked(m_property.toBool());
connect(&m_property, &Property::valueChangedBool, this, &PropertyCheckBox::setChecked);
connect(this, &PropertyCheckBox::toggled, &m_property, &Property::setValueBool);
connect(&m_property, &Property::valueChangedBool,
[this](bool value)
{
InternalUpdateHolder hold(m_internalUpdate);
setChecked(value);
});
connect(&m_property, &Property::attributeChanged,
[this](AttributeName name, const QVariant& value)
{
switch(name)
{
case AttributeName::Enabled:
setEnabled(value.toBool());
break;
case AttributeName::Visible:
setVisible(value.toBool());
break;
default:
break;
}
});
connect(this, &PropertyCheckBox::toggled,
[this](bool value)
{
if(!m_internalUpdate)
m_property.setValueBool(value);
});
}

Datei anzeigen

@ -31,6 +31,7 @@ class PropertyCheckBox : public QCheckBox
{
protected:
Property& m_property;
bool m_internalUpdate;
public:
PropertyCheckBox(Property& property, QWidget* parent = nullptr);

Datei anzeigen

@ -34,21 +34,36 @@ PropertyDirectionControl::PropertyDirectionControl(Property& property, QWidget*
{
Q_ASSERT(property.enumName() == EnumName<Direction>::value);
QHBoxLayout* l = new QHBoxLayout();
setEnabled(m_property.getAttributeBool(AttributeName::Enabled, true));
setVisible(m_property.getAttributeBool(AttributeName::Visible, true));
m_reverse->setArrowType(Qt::LeftArrow);
//m_reverse->setCheckable(true);
m_forward->setArrowType(Qt::RightArrow);
//m_forward->setCheckable(true);
QHBoxLayout* l = new QHBoxLayout();
l->addWidget(m_reverse);
l->addWidget(m_forward);
setLayout(l);
setValue(m_property.toInt64());
connect(&m_property, &Property::valueChangedInt64, this, &PropertyDirectionControl::setValue);
connect(&m_property, &Property::attributeChanged,
[this](AttributeName name, const QVariant& value)
{
switch(name)
{
case AttributeName::Enabled:
setEnabled(value.toBool());
break;
case AttributeName::Visible:
setVisible(value.toBool());
break;
default:
break;
}
});
connect(m_reverse, &QToolButton::clicked, this, &PropertyDirectionControl::buttonClicked);
connect(m_forward, &QToolButton::clicked, this, &PropertyDirectionControl::buttonClicked);
}

Datei anzeigen

@ -27,8 +27,27 @@ PropertyLineEdit::PropertyLineEdit(Property& property, QWidget* parent) :
QLineEdit(parent),
m_property{property}
{
Q_ASSERT(m_property.type() == PropertyType::String);
Q_ASSERT(m_property.type() == ValueType::String);
setEnabled(m_property.getAttributeBool(AttributeName::Enabled, true));
setVisible(m_property.getAttributeBool(AttributeName::Visible, true));
setText(m_property.toString());
connect(&m_property, &Property::valueChangedString, this, &PropertyLineEdit::setText);
connect(&m_property, &Property::attributeChanged,
[this](AttributeName name, const QVariant& value)
{
switch(name)
{
case AttributeName::Enabled:
setEnabled(value.toBool());
break;
case AttributeName::Visible:
setVisible(value.toBool());
break;
default:
break;
}
});
connect(this, &PropertyLineEdit::textEdited, &m_property, &Property::setValueString);
}

Datei anzeigen

@ -27,9 +27,28 @@ PropertySpinBox::PropertySpinBox(AbstractProperty& property, QWidget* parent) :
QSpinBox(parent),
m_property{property}
{
Q_ASSERT(m_property.type() == PropertyType::Integer);
Q_ASSERT(m_property.type() == ValueType::Integer);
setEnabled(m_property.getAttributeBool(AttributeName::Enabled, true));
setVisible(m_property.getAttributeBool(AttributeName::Visible, true));
setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
setValue(m_property.toInt());
connect(&m_property, &AbstractProperty::valueChangedInt, this, &PropertySpinBox::setValue);
connect(&m_property, &AbstractProperty::attributeChanged,
[this](AttributeName name, const QVariant& value)
{
switch(name)
{
case AttributeName::Enabled:
setEnabled(value.toBool());
break;
case AttributeName::Visible:
setVisible(value.toBool());
break;
default:
break;
}
});
connect(this, QOverload<int>::of(&PropertySpinBox::valueChanged), &m_property, &AbstractProperty::setValueInt);
}

Datei anzeigen

@ -27,8 +27,25 @@ PropertyTextEdit::PropertyTextEdit(Property& property, QWidget* parent) :
QTextEdit(parent),
m_property{property}
{
Q_ASSERT(m_property.type() == PropertyType::String);
Q_ASSERT(m_property.type() == ValueType::String);
setPlainText(m_property.toString());
connect(&m_property, &Property::valueChangedString, this, &PropertyTextEdit::setPlainText);
connect(&m_property, &Property::attributeChanged,
[this](AttributeName name, const QVariant& value)
{
switch(name)
{
case AttributeName::Enabled:
setEnabled(value.toBool());
break;
case AttributeName::Visible:
setVisible(value.toBool());
break;
default:
break;
}
});
connect(this, &PropertyTextEdit::textChanged, [this](){ m_property.setValueString(toPlainText()); });
}

Datei anzeigen

@ -43,7 +43,8 @@ SOURCES += \
src/widget/propertylineedit.cpp \
src/widget/propertyspinbox.cpp \
src/widget/propertydirectioncontrol.cpp \
src/widget/propertytextedit.cpp
src/widget/propertytextedit.cpp \
src/subwindow/objecteditsubwindow.cpp
HEADERS += \
src/mainwindow.hpp \
@ -77,7 +78,8 @@ HEADERS += \
src/widget/propertylineedit.hpp \
src/widget/propertyspinbox.hpp \
src/widget/propertydirectioncontrol.hpp \
src/widget/propertytextedit.hpp
src/widget/propertytextedit.hpp \
src/subwindow/objecteditsubwindow.hpp
RESOURCES += \
dark.qrc

Datei anzeigen

@ -0,0 +1,15 @@
#include "abstractattribute.hpp"
#include "interfaceitem.hpp"
#include "object.hpp"
AbstractAttribute::AbstractAttribute(InterfaceItem& item, AttributeName name, ValueType type) :
m_item{item},
m_name{name},
m_type{type}
{
}
void AbstractAttribute::changed()
{
m_item.object().attributeChanged(*this);
}

Datei anzeigen

@ -0,0 +1,32 @@
#ifndef ABSTRACTATTRIBUTE_HPP
#define ABSTRACTATTRIBUTE_HPP
#include <string>
#include <enum/attributename.hpp>
#include <enum/valuetype.hpp>
class InterfaceItem;
class AbstractAttribute
{
protected:
InterfaceItem& m_item;
const AttributeName m_name;
const ValueType m_type;
void changed();
public:
AbstractAttribute(InterfaceItem& item, AttributeName name, ValueType type);
inline InterfaceItem& item() const { return m_item; }
inline AttributeName name() const { return m_name; }
inline ValueType type() const { return m_type; }
virtual bool toBool() const = 0;
virtual int64_t toInt64() const = 0;
virtual double toDouble() const = 0;
virtual std::string toString() const = 0;
};
#endif

Datei anzeigen

@ -29,5 +29,5 @@ void AbstractProperty::changed()
{
std::cout << "AbstractProperty::changed" << std::endl;
m_object.propertyChanged(m_object, *this);
m_object.propertyChanged(*this);
}

Datei anzeigen

@ -24,7 +24,7 @@
#define SERVER_CORE_ABSTRACTPROPERTY_HPP
#include "interfaceitem.hpp"
#include <enum/propertytype.hpp>
#include <enum/valuetype.hpp>
#include "propertyflags.hpp"
#include "objectptr.hpp"
#include <cassert>
@ -33,18 +33,18 @@
class AbstractProperty : public InterfaceItem
{
protected:
const PropertyType m_type;
const ValueType m_type;
PropertyFlags m_flags;
void changed();
public:
AbstractProperty(Object& object, const std::string& name, PropertyType type, PropertyFlags flags) :
AbstractProperty(Object& object, const std::string& name, ValueType type, PropertyFlags flags) :
InterfaceItem{object, name},
m_type{type},
m_flags{flags}
{
assert(type != PropertyType::Invalid);
assert(type != ValueType::Invalid);
assert(is_access_valid(flags) && is_store_valid(flags));
}
@ -63,7 +63,7 @@ class AbstractProperty : public InterfaceItem
return true;//!is_empty(m_flags & PropertyFlags::WriteOnly);
}
PropertyType type() const
ValueType type() const
{
return m_type;
}

Datei anzeigen

@ -0,0 +1,51 @@
#ifndef INTERFACEITEMATTRIBUTE_HPP
#define INTERFACEITEMATTRIBUTE_HPP
#include "abstractattribute.hpp"
#include "to.hpp"
#include "valuetypetraits.hpp"
template<typename T>
class Attribute : public AbstractAttribute
{
protected:
T m_value;
public:
Attribute(InterfaceItem& item, AttributeName name, const T& value) :
AbstractAttribute{item, name, value_type_v<T>},
m_value{value}
{
}
bool toBool() const final
{
return to<bool>(m_value);
}
int64_t toInt64() const final
{
return to<int64_t>(m_value);
}
double toDouble() const final
{
return to<double>(m_value);
}
std::string toString() const final
{
return to<std::string>(m_value);
}
void setValue(const T& value)
{
if(m_value != value)
{
m_value = value;
changed();
}
}
};
#endif

Datei anzeigen

@ -36,7 +36,8 @@ IdObject::IdObject(const std::weak_ptr<World> world, const std::string& _id) :
return true;
}}
{
m_interfaceItems.add(id);
m_interfaceItems.add(id)
.addAttributeEnabled(false);
}
IdObject::~IdObject()
@ -50,3 +51,8 @@ void IdObject::addToWorld()
if(auto world = m_world.lock())
world->m_objects.emplace(id, weak_from_this());
}
void IdObject::modeChanged(TraintasticMode mode)
{
id.setAttributeEnabled(mode == TraintasticMode::Edit);
}

Datei anzeigen

@ -25,6 +25,7 @@
#include "object.hpp"
#include "property.hpp"
#include <enum/traintasticmode.hpp>
#define CREATE(T) \
static std::shared_ptr<T> create(const std::weak_ptr<World>& world, const std::string& _id) \
@ -43,6 +44,7 @@ class IdObject : public Object
IdObject(const std::weak_ptr<World> world, const std::string& _id);
void addToWorld();
void modeChanged(TraintasticMode mode) override;
public:
Property<std::string> id;

Datei anzeigen

@ -23,15 +23,36 @@
#ifndef SERVER_CORE_INTERFACEITEM_HPP
#define SERVER_CORE_INTERFACEITEM_HPP
#include <unordered_map>
#include <string>
#include <memory>
#include "attribute.hpp"
#include <enum/category.hpp>
class Object;
class InterfaceItem
{
public:
using Attributes = std::unordered_map<AttributeName, std::unique_ptr<AbstractAttribute>>;
protected:
Object& m_object;
const std::string m_name;
Attributes m_attributes;
template<typename T>
InterfaceItem& addAttribute(AttributeName name, const T& value)
{
m_attributes.emplace(name, std::make_unique<Attribute<T>>(*this, name, value));
return *this;
}
template<typename T>
void setAttribute(AttributeName name, const T& value)
{
static_cast<Attribute<T>*>(m_attributes[name].get())->setValue(value);
}
public:
InterfaceItem(Object& object, const std::string& name) :
@ -53,6 +74,17 @@ class InterfaceItem
{
return m_name;
}
const Attributes& attributes() const
{
return m_attributes;
}
inline InterfaceItem& addAttributeCategory(Category value) { return addAttribute(AttributeName::Category, value); }
inline InterfaceItem& addAttributeEnabled(bool value) { return addAttribute(AttributeName::Enabled, value); }
inline InterfaceItem& addAttributeVisible(bool value) { return addAttribute(AttributeName::Visible, value); }
inline void setAttributeEnabled(bool value) { setAttribute(AttributeName::Enabled, value); }
};
#endif

Datei anzeigen

@ -22,6 +22,7 @@
#include "interfaceitems.hpp"
#include "interfaceitem.hpp"
#include <algorithm>
InterfaceItem* InterfaceItems::find(const std::string& name) const
{
@ -29,8 +30,16 @@ InterfaceItem* InterfaceItems::find(const std::string& name) const
return (it != m_items.end()) ? &it->second : nullptr;
}
void InterfaceItems::add(InterfaceItem& item)
InterfaceItem& InterfaceItems::add(InterfaceItem& item)
{
m_items.emplace(item.name(), item);
m_itemOrder.push_back(item.name());
return item;
}
InterfaceItem& InterfaceItems::insertBefore(InterfaceItem& item, const InterfaceItem& before)
{
m_items.emplace(item.name(), item);
m_itemOrder.insert(std::find(m_itemOrder.begin(), m_itemOrder.end(), before.name()), item.name());
return item;
}

Datei anzeigen

@ -44,7 +44,9 @@ class InterfaceItems
const std::list<std::string>& names() const { return m_itemOrder; }
InterfaceItem* find(const std::string& name) const;
void add(InterfaceItem& item);
InterfaceItem& add(InterfaceItem& item);
InterfaceItem& insertBefore(InterfaceItem& item, const InterfaceItem& before);
inline InterfaceItem& operator[](const std::string& name) const { return m_items.at(name); }
};

Datei anzeigen

@ -26,6 +26,7 @@
#include "objectptr.hpp"
#include <boost/signals2/signal.hpp>
#include "interfaceitems.hpp"
#include <enum/traintasticmode.hpp>
#define CLASS_ID(id) \
public: \
@ -34,17 +35,23 @@
class AbstractMethod;
class AbstractProperty;
class AbstractAttribute;
class Object : public std::enable_shared_from_this<Object>
{
friend class World;
protected:
InterfaceItems m_interfaceItems;
//void log(LogLevel level, const std::string& message) const;
//inline void logError(const std::string& message) const { log(LogLevel::Error, message); }
virtual void modeChanged(TraintasticMode) {}
public:
boost::signals2::signal<void (Object& object, AbstractProperty&)> propertyChanged;
boost::signals2::signal<void (AbstractProperty&)> propertyChanged;
boost::signals2::signal<void (AbstractAttribute&)> attributeChanged;
Object();
virtual ~Object();

Datei anzeigen

@ -39,7 +39,7 @@ class ObjectProperty : public AbstractProperty
public:
ObjectProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags) :
AbstractProperty(*object, name, PropertyType::Object, flags),
AbstractProperty(*object, name, ValueType::Object, flags),
m_value{value}
{
}

Datei anzeigen

@ -24,7 +24,7 @@
#define SERVER_CORE_PROPERTY_HPP
#include "abstractproperty.hpp"
#include "propertytypetraits.hpp"
#include "valuetypetraits.hpp"
#include "to.hpp"
#include <functional>
#include <enum/enum.hpp>
@ -43,7 +43,7 @@ class Property : public AbstractProperty
public:
Property(Object* object, const std::string& name, const T& value, PropertyFlags flags) :
AbstractProperty(*object, name, property_type<T>::value, flags),
AbstractProperty(*object, name, value_type<T>::value, flags),
m_value{value}
{
//static_assert(property_type<T>::value != PropertyType::Invalid);
@ -90,6 +90,15 @@ class Property : public AbstractProperty
throw invalid_value_error();
}
void setValueInternal(T value)
{
if(m_value != value)
{
m_value = value;
changed();
}
}
operator const T&() const
{
return m_value;

Datei anzeigen

@ -25,6 +25,7 @@
#include "traintastic.hpp"
#include "client.hpp"
#include "abstractproperty.hpp"
#include "abstractattribute.hpp"
#include <enum/interfaceitemtype.hpp>
#include "tablemodel.hpp"
#include "world.hpp"
@ -162,6 +163,8 @@ bool Session::processMessage(const Message& message)
Handle handle = message.read<Handle>();
Traintastic::instance->console->debug(m_client->m_id, "ReleaseObject: " + std::to_string(handle));
m_handles.removeHandle(handle);
m_propertyChanged.erase(handle);
m_attributeChanged.erase(handle);
break;
}
case Message::Command::ObjectSetProperty:
@ -172,21 +175,21 @@ bool Session::processMessage(const Message& message)
{
try
{
switch(message.read<PropertyType>())
switch(message.read<ValueType>())
{
case PropertyType::Boolean:
case ValueType::Boolean:
property->fromBool(message.read<bool>());
break;
case PropertyType::Integer:
case ValueType::Integer:
property->fromInt64(message.read<int64_t>());
break;
case PropertyType::Float:
case ValueType::Float:
property->fromDouble(message.read<double>());
break;
case PropertyType::String:
case ValueType::String:
property->fromString(message.read<std::string>());
break;
}
@ -194,7 +197,7 @@ bool Session::processMessage(const Message& message)
catch(const std::exception&)
{
// set property failed, send changed event with current value:
objectPropertyChanged(*object, *property);
objectPropertyChanged(*property);
}
}
}
@ -239,7 +242,8 @@ void Session::writeObject(Message& message, const ObjectPtr& object)
handle = m_handles.addItem(object);
m_propertyChanged.emplace(handle, object->propertyChanged.connect(std::bind(&Session::objectPropertyChanged, this, std::placeholders::_1, std::placeholders::_2)));
m_propertyChanged.emplace(handle, object->propertyChanged.connect(std::bind(&Session::objectPropertyChanged, this, std::placeholders::_1)));
m_attributeChanged.emplace(handle, object->attributeChanged.connect(std::bind(&Session::objectAttributeChanged, this, std::placeholders::_1)));
message.write(handle);
message.write(object->getClassId());
@ -261,28 +265,28 @@ void Session::writeObject(Message& message, const ObjectPtr& object)
message.write(property->type());
switch(property->type())
{
case PropertyType::Boolean:
case ValueType::Boolean:
message.write(property->toBool());
break;
case PropertyType::Enum:
case ValueType::Enum:
message.write(property->toInt64());
message.write(property->enumName());
break;
case PropertyType::Integer:
case ValueType::Integer:
message.write(property->toInt64());
break;
case PropertyType::Float:
case ValueType::Float:
message.write(property->toDouble());
break;
case PropertyType::String:
case ValueType::String:
message.write(property->toString());
break;
case PropertyType::Object:
case ValueType::Object:
{
ObjectPtr obj = property->toObject();
// TODO: assert(!obj || dynamic_cast<IdObject*>(obj.get()));
@ -298,6 +302,41 @@ void Session::writeObject(Message& message, const ObjectPtr& object)
}
}
message.writeBlock(); // attributes
for(const auto& it : item.attributes())
{
const AbstractAttribute& attribute = *it.second;
message.writeBlock(); // attribute
message.write(attribute.name());
message.write(attribute.type());
switch(attribute.type())
{
case ValueType::Boolean:
message.write(attribute.toBool());
break;
case ValueType::Enum:
case ValueType::Integer:
message.write(attribute.toInt64());
break;
case ValueType::Float:
message.write(attribute.toDouble());
break;
case ValueType::String:
message.write(attribute.toString());
break;
default:
assert(false);
break;
}
message.writeBlockEnd(); // end attribute
}
message.writeBlockEnd(); // end attributes
message.writeBlockEnd(); // end item
}
message.writeBlockEnd(); // end items
@ -322,32 +361,63 @@ void Session::writeTableModel(Message& message, const TableModelPtr& model)
message.writeBlockEnd(); // end model
}
void Session::objectPropertyChanged(Object& object, AbstractProperty& property)
void Session::objectPropertyChanged(AbstractProperty& property)
{
std::cout << "objectPropertyChanged " << property.name() << std::endl;
auto event = Message::newEvent(Message::Command::ObjectPropertyChanged);
event->write(m_handles.getHandle(object.shared_from_this()));
event->write(m_handles.getHandle(property.object().shared_from_this()));
event->write(property.name());
event->write(property.type());
switch(property.type())
{
case PropertyType::Boolean:
case ValueType::Boolean:
event->write(property.toBool());
break;
case PropertyType::Enum:
case PropertyType::Integer:
case ValueType::Enum:
case ValueType::Integer:
event->write(property.toInt64());
break;
case PropertyType::Float:
case ValueType::Float:
event->write(property.toDouble());
break;
case PropertyType::String:
case ValueType::String:
event->write(property.toString());
break;
}
m_client->sendMessage(std::move(event));
}
void Session::objectAttributeChanged(AbstractAttribute& attribute)
{
std::cout << "objectAttributeChanged " << attribute.item().name() << "." << (int)attribute.name() << std::endl;
auto event = Message::newEvent(Message::Command::ObjectAttributeChanged);
event->write(m_handles.getHandle(attribute.item().object().shared_from_this()));
event->write(attribute.item().name());
event->write(attribute.name());
event->write(attribute.type());
switch(attribute.type())
{
case ValueType::Boolean:
event->write(attribute.toBool());
break;
case ValueType::Enum:
case ValueType::Integer:
//event->write(attribute.toInt64());
break;
case ValueType::Float:
//event->write(attribute.toDouble());
break;
case ValueType::String:
//event->write(attribute.toString());
break;
}
m_client->sendMessage(std::move(event));
}

Datei anzeigen

@ -33,6 +33,7 @@
class Client;
class AbstractProperty;
class AbstractAttribute;
class Session : public std::enable_shared_from_this<Session>
{
@ -46,13 +47,15 @@ class Session : public std::enable_shared_from_this<Session>
boost::uuids::uuid m_uuid;
Handles m_handles;
std::unordered_map<Handle, boost::signals2::connection> m_propertyChanged;
std::unordered_map<Handle, boost::signals2::connection> m_attributeChanged;
bool processMessage(const Message& message);
void writeObject(Message& message, const ObjectPtr& object);
void writeTableModel(Message& message, const TableModelPtr& model);
void objectPropertyChanged(Object& object, AbstractProperty& property);
void objectPropertyChanged(AbstractProperty& property);
void objectAttributeChanged(AbstractAttribute& attribute);
public:
Session(const std::shared_ptr<Client>& client);

Datei anzeigen

@ -50,7 +50,7 @@ Traintastic::Traintastic(const std::filesystem::path& dataDir) :
{
assert(world);
console->info(id, "Mode changed to <TODO> " + std::to_string((int)value));
//world->modeChanged(value);
world->modeChanged(value);
},
[this](TraintasticMode& newValue)
{

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/core/propertytypetraits.hpp
* server/src/core/valuetypetraits.hpp
*
* This file is part of the traintastic source code.
*
@ -20,22 +20,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SERVER_CORE_PROPERTYTYPETRAITS_HPP
#define SERVER_CORE_PROPERTYTYPETRAITS_HPP
#ifndef SERVER_CORE_VALUETYPETRAITS_HPP
#define SERVER_CORE_VALUETYPETRAITS_HPP
#include <enum/propertytype.hpp>
#include <enum/valuetype.hpp>
#include "objectptr.hpp"
template<typename T>
struct property_type
struct value_type
{
static constexpr PropertyType value =
std::is_same<T, bool>::value ? PropertyType::Boolean : (
std::is_enum<T>::value ? PropertyType::Enum : (
std::is_integral<T>::value ? PropertyType::Integer : (
std::is_floating_point<T>::value ? PropertyType::Float : (
std::is_same<T, std::string>::value ? PropertyType::String : (
PropertyType::Invalid)))));
static constexpr ValueType value =
std::is_same_v<T, bool> ? ValueType::Boolean : (
std::is_enum_v<T> ? ValueType::Enum : (
std::is_integral_v<T> ? ValueType::Integer : (
std::is_floating_point_v<T> ? ValueType::Float : (
std::is_same_v<T, std::string> ? ValueType::String : (
ValueType::Invalid)))));
};
template<typename T>
inline constexpr ValueType value_type_v = value_type<T>::value;
#endif

Datei anzeigen

@ -103,6 +103,14 @@ ObjectPtr World::getObject(const std::string& _id) const
return ObjectPtr();
}
void World::modeChanged(TraintasticMode mode)
{
Traintastic::instance->console->debug(id, "World::modeChanged");
for(auto& it : m_objects)
it.second.lock()->modeChanged(mode);
}
void World::load()
{
std::ifstream file(m_filename);
@ -218,7 +226,7 @@ void World::save()
for(auto& item : object->interfaceItems())
if(AbstractProperty* property = dynamic_cast<AbstractProperty*>(&item.second))
{
if(property->type() == PropertyType::Object)
if(property->type() == ValueType::Object)
{
if(IdObject* idObject = dynamic_cast<IdObject*>(property->toObject().get()))
objectData[property->name()] = idObject->id.toJSON();

Datei anzeigen

@ -37,6 +37,7 @@
class World : public Object
{
friend class IdObject;
friend class Traintastic;
protected:
static const std::string id;
@ -47,6 +48,7 @@ class World : public Object
boost::uuids::uuid m_uuid;
std::unordered_map<std::string, std::weak_ptr<Object>> m_objects;
void modeChanged(TraintasticMode mode);
void load();
public:

Datei anzeigen

@ -40,7 +40,8 @@ CommandStation::CommandStation(const std::weak_ptr<World>& world, const std::str
m_interfaceItems.add(online);
m_interfaceItems.add(status);
m_interfaceItems.add(decoders);
m_interfaceItems.add(notes);
m_interfaceItems.add(notes)
.addAttributeCategory(Category::Notes);
}
const std::shared_ptr<Decoder>& CommandStation::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const

Datei anzeigen

@ -39,14 +39,16 @@
#define Z21_LAN_X_UNKNOWN_COMMAND 0x82
#define Z21_LAN_X_BC_STOPPED 0x81
#define Z21_LAN_X_LOCO_INFO 0xEF
#define Z21_LAN_SET_BROADCASTFLAGS 0x50
#define Z21_LAN_GET_BROADCASTFLAGS 0x51
#define Z21_LAN_SYSTEMSTATE_DATACHANGED 0x84
#define Z21_LAN_SYSTEMSTATE_GETDATA 0x85
#define Z21_HWT_Z21_OLD 0x00000200 //!< „black Z21” (hardware variant from 2012)
#define Z21_HWT_Z21_NEW 0x00000201 //!< „black Z21”(hardware variant from 2013)
#define Z21_HWT_SMARTRAIL 0x00000202 //!< SmartRail (from 2012)
#define Z21_HWT_z21_SMALL 0x00000203 //!< „white z21” starter set variant (from 2013)
#define Z21_HWT_z21_START 0x00000204 //!< „z21 start” starter set variant (from 2016)
#define Z21_HWT_Z21_SMALL 0x00000203 //!< „white z21” starter set variant (from 2013)
#define Z21_HWT_Z21_START 0x00000204 //!< „z21 start” starter set variant (from 2016)
#define Z21_CENTRALSTATE_EMERGENCYSTOP 0x01 //!< The emergency stop is switched on
#define Z21_CENTRALSTATE_TRACKVOLTAGEOFF 0x02 //!< The track voltage is switched off
@ -153,6 +155,23 @@ struct z21_lan_x_set_track_power_on : z21_lan_x
}
} __attribute__((packed));
struct z21_lan_x_get_loco_info : z21_lan_x
{
uint8_t db0;
uint8_t addressHigh;
uint8_t addressLow;
uint8_t checksum;
z21_lan_x_get_loco_info() :
db0{0xF0}
{
dataLen = sizeof(z21_lan_x_get_loco_info);
header = Z21_LAN_X;
xheader = 0xE3;
}
} __attribute__((packed));
//static_assert(sizeof(z21_lan_x_get_loco_info) == 0x07);
struct z21_lan_x_loco_info : z21_lan_x
{
uint8_t addressHigh;
@ -197,6 +216,29 @@ struct z21_lan_systemstate_getdata : z21_lan_header
} __attribute__((packed));
static_assert(sizeof(z21_lan_systemstate_getdata) == 0x04);
struct z21_lan_set_broadcastflags : z21_lan_header
{
uint32_t broadcastFlags; // LE
z21_lan_set_broadcastflags(uint32_t _broadcastFlags = 0) :
broadcastFlags{_broadcastFlags}
{
dataLen = sizeof(z21_lan_set_broadcastflags);
header = Z21_LAN_SET_BROADCASTFLAGS;
}
} __attribute__((packed));
static_assert(sizeof(z21_lan_set_broadcastflags) == 0x08);
struct z21_lan_get_broadcastflags : z21_lan_header
{
z21_lan_get_broadcastflags()
{
dataLen = sizeof(z21_lan_get_broadcastflags);
header = Z21_LAN_GET_BROADCASTFLAGS;
}
} __attribute__((packed));
static_assert(sizeof(z21_lan_get_broadcastflags) == 0x04);
struct z21_lan_systemstate_datachanged : z21_lan_header
{
int16_t mainCurrent; //!< Current on the main track in mA

Datei anzeigen

@ -63,33 +63,43 @@ Z21::Z21(const std::weak_ptr<World>& world, const std::string& _id) :
m_socket{Traintastic::instance->ioContext()},
hostname{this, "hostname", "", PropertyFlags::AccessWCC},
port{this, "port", 21105, PropertyFlags::AccessWCC},
serialNumber{this, "serial_number", 0, PropertyFlags::AccessRRR},
serialNumber{this, "serial_number", "", PropertyFlags::AccessRRR},
hardwareType{this, "hardware_type", "", PropertyFlags::AccessRRR},
firmwareVersion{this, "firmware_version", "", PropertyFlags::AccessRRR},
emergencyStop{this, "emergency_stop", false, PropertyFlags::TODO,
[this](bool value)
{
if(value)
if(online && value)
send(z21_lan_x_set_stop());
}},
trackVoltageOff{this, "track_voltage_off", false, PropertyFlags::TODO,
[this](bool value)
{
if(value)
send(z21_lan_x_set_track_power_off());
else
send(z21_lan_x_set_track_power_on());
if(online)
{
if(value)
send(z21_lan_x_set_track_power_off());
else
send(z21_lan_x_set_track_power_on());
}
}}
{
name = "Z21";
m_interfaceItems.add(hostname);
m_interfaceItems.add(port);
m_interfaceItems.add(serialNumber);
m_interfaceItems.add(hardwareType);
m_interfaceItems.add(firmwareVersion);
m_interfaceItems.add(emergencyStop);
m_interfaceItems.add(trackVoltageOff);
m_interfaceItems.insertBefore(hostname, notes)
.addAttributeEnabled(true);
m_interfaceItems.insertBefore(port, notes)
.addAttributeEnabled(true);
m_interfaceItems.insertBefore(serialNumber, notes)
.addAttributeCategory(Category::Info);
m_interfaceItems.insertBefore(hardwareType, notes)
.addAttributeCategory(Category::Info);
m_interfaceItems.insertBefore(firmwareVersion, notes)
.addAttributeCategory(Category::Info);
m_interfaceItems.insertBefore(emergencyStop, notes)
.addAttributeEnabled(false);
m_interfaceItems.insertBefore(trackVoltageOff, notes)
.addAttributeEnabled(false);
}
bool Z21::isDecoderSupported(Decoder& decoder) const
@ -194,13 +204,41 @@ bool Z21::setOnline(bool& value)
Traintastic::instance->console->error(id, "socket.bind: " + ec.message());
return false;
}
receive();
send(z21_lan_set_broadcastflags(/*0x00010000 |*/ 0x00000100 | 0x00000001));
// try to communicate with Z21
send(z21_lan_get_broadcastflags());
send(z21_lan_get_serial_number());
send(z21_lan_get_hwinfo());
send(z21_lan_systemstate_getdata());
/*
for(auto& decoder : decoders)
{
z21_lan_x_get_loco_info cmd;
send(cmd);
}
*/
hostname.setAttributeEnabled(false);
port.setAttributeEnabled(false);
emergencyStop.setAttributeEnabled(true);
trackVoltageOff.setAttributeEnabled(true);
}
else if(m_socket.is_open() && !value)
{
send(z21_lan_logoff());
serialNumber = "";
hardwareType = "";
firmwareVersion = "";
hostname.setAttributeEnabled(true);
port.setAttributeEnabled(true);
emergencyStop.setAttributeEnabled(false);
trackVoltageOff.setAttributeEnabled(false);
m_socket.close();
}
return true;
@ -221,9 +259,9 @@ void Z21::receive()
case Z21_LAN_GET_SERIAL_NUMBER:
{
EventLoop::call(
[this, value=static_cast<const z21_lan_get_serial_number_reply*>(cmd)->serialNumber]()
[this, value=std::to_string(static_cast<const z21_lan_get_serial_number_reply*>(cmd)->serialNumber)]()
{
serialNumber = value;
serialNumber.setValueInternal(value);
});
break;
}
@ -243,10 +281,10 @@ void Z21::receive()
case Z21_HWT_SMARTRAIL:
hwType = "SmartRail (from 2012)";
break;
case Z21_HWT_z21_SMALL:
case Z21_HWT_Z21_SMALL:
hwType = "White Z21 (starter set variant from 2013)";
break;
case Z21_HWT_z21_START :
case Z21_HWT_Z21_START :
hwType = "Z21 start (starter set variant from 2016)";
break;
default:
@ -259,8 +297,8 @@ void Z21::receive()
EventLoop::call(
[this, hwType, fwVersion]()
{
hardwareType = hwType;
firmwareVersion = fwVersion;
hardwareType.setValueInternal(hwType);
firmwareVersion.setValueInternal(fwVersion);
});
break;
}
@ -294,13 +332,13 @@ void Z21::receive()
if((speedStepMode == 0 && decoder->speedSteps == 14) ||
(speedStepMode == 2 && decoder->speedSteps == 28) ||
(speedStepMode == 4 && decoder->speedSteps == 126))
decoder->speedStep = speedStep;
decoder->speedStep.setValueInternal(speedStep);
for(auto& function : *decoder->functions)
{
const uint8_t number = function->number;
if(number <= 28)
function->value = functions & (1 << number);
function->value.setValueInternal(functions & (1 << number));
}
}
});
@ -315,7 +353,7 @@ void Z21::receive()
EventLoop::call(
[this]()
{
emergencyStop = true;
emergencyStop.setValueInternal(true);
});
break;
@ -331,8 +369,8 @@ void Z21::receive()
EventLoop::call(
[this, state]()
{
emergencyStop = state.centralState & Z21_CENTRALSTATE_EMERGENCYSTOP;
trackVoltageOff = state.centralState & Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
emergencyStop.setValueInternal(state.centralState & Z21_CENTRALSTATE_EMERGENCYSTOP);
trackVoltageOff.setValueInternal(state.centralState & Z21_CENTRALSTATE_TRACKVOLTAGEOFF);
});
break;
}

Datei anzeigen

@ -56,7 +56,7 @@ class Z21 : public CommandStation
Property<std::string> hostname;
Property<uint16_t> port;
Property<uint32_t> serialNumber;
Property<std::string> serialNumber;
Property<std::string> hardwareType;
Property<std::string> firmwareVersion;
Property<bool> emergencyStop;

Datei anzeigen

@ -81,13 +81,20 @@ Decoder::Decoder(const std::weak_ptr<World>& world, const std::string& _id) :
notes{this, "notes", "", PropertyFlags::AccessWWW}
{
m_interfaceItems.add(name);
m_interfaceItems.add(commandStation);
m_interfaceItems.add(protocol);
m_interfaceItems.add(address);
m_interfaceItems.add(emergencyStop);
m_interfaceItems.add(direction);
m_interfaceItems.add(speedSteps);
m_interfaceItems.add(speedStep);
m_interfaceItems.add(commandStation)
.addAttributeEnabled(false);
m_interfaceItems.add(protocol)
.addAttributeEnabled(false);
m_interfaceItems.add(address)
.addAttributeEnabled(false);
m_interfaceItems.add(emergencyStop)
.addAttributeEnabled(false);
m_interfaceItems.add(direction)
.addAttributeEnabled(false);
m_interfaceItems.add(speedSteps)
.addAttributeEnabled(false);
m_interfaceItems.add(speedStep)
.addAttributeEnabled(false);
m_interfaceItems.add(functions);
m_interfaceItems.add(notes);
}
@ -101,6 +108,20 @@ const std::shared_ptr<DecoderFunction>& Decoder::getFunction(uint32_t number) co
return DecoderFunction::null;
}
void Decoder::modeChanged(TraintasticMode mode)
{
IdObject::modeChanged(mode);
commandStation.setAttributeEnabled(mode == TraintasticMode::Edit);
protocol.setAttributeEnabled(mode == TraintasticMode::Edit);
address.setAttributeEnabled(mode == TraintasticMode::Edit);
speedSteps.setAttributeEnabled(mode == TraintasticMode::Edit);
speedStep.setAttributeEnabled(mode == TraintasticMode::Run);
if(mode == TraintasticMode::Edit)
speedStep = 0;
}
void Decoder::changed(DecoderChangeFlags changes, uint32_t functionNumber)
{
if(commandStation)

Datei anzeigen

@ -46,6 +46,7 @@ class Decoder : public IdObject
friend class DecoderFunction;
protected:
void modeChanged(TraintasticMode mode) final;
void changed(DecoderChangeFlags changes, uint32_t functionNumber = 0);
public:

Datei anzeigen

@ -0,0 +1,6 @@
#ifndef ATTRIBUTES_HPP
#define ATTRIBUTES_HPP
#endif // ATTRIBUTES_HPP

Datei anzeigen

@ -0,0 +1,38 @@
/**
* shared/src/enum/propertytype.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 Reinder Feenstra
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SHARED_ENUM_ATTIRBUTE_HPP
#define SHARED_ENUM_ATTIRBUTE_HPP
#include <cstdint>
enum class AttributeName : uint16_t
{
Visible = 0,
Enabled = 1,
Min = 2,
Max = 3,
Category = 4,
};
#endif

37
shared/src/enum/category.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,37 @@
/**
* shared/src/enum/category.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019 Reinder Feenstra
*
* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SHARED_ENUM_CATEGORY_HPP
#define SHARED_ENUM_CATEGORY_HPP
#include <cstdint>
enum class Category : uint16_t
{
General = 0,
Notes = 1,
Status = 2,
Info = 3,
};
#endif

Datei anzeigen

@ -1,5 +1,5 @@
/**
* shared/src/enum/propertytype.hpp
* shared/src/enum/valuetype.hpp
*
* This file is part of the traintastic source code.
*
@ -20,12 +20,12 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef SHARED_ENUM_PROPERTYTYPE_HPP
#define SHARED_ENUM_PROPERTYTYPE_HPP
#ifndef SHARED_ENUM_VALUETYPE_HPP
#define SHARED_ENUM_VALUETYPE_HPP
#include <cstdint>
enum class PropertyType : uint8_t
enum class ValueType : uint8_t
{
Invalid = 0,
Boolean = 1,

Datei anzeigen

@ -59,12 +59,14 @@ class Message
ReleaseObject = 15,
ObjectSetProperty = 16,
ObjectPropertyChanged = 17,
GetTableModel = 18,
ReleaseTableModel = 19,
TableModelColumnHeadersChanged = 20,
TableModelRowCountChanged = 21,
TableModelSetRegion = 22,
TableModelUpdateRegion = 23,
ObjectAttributeChanged = 18,
GetTableModel = 19,
ReleaseTableModel = 20,
TableModelColumnHeadersChanged = 21,
TableModelRowCountChanged = 22,
TableModelSetRegion = 23,
TableModelUpdateRegion = 24,
Discover = 255,
};