Replaced command station and controller by interface with supporting controllers

Dieser Commit ist enthalten in:
Reinder Feenstra 2021-10-17 16:10:56 +02:00
Ursprung 48d53f60f2
Commit 956c8ad168
69 geänderte Dateien mit 1965 neuen und 1322 gelöschten Zeilen

Datei anzeigen

@ -225,11 +225,10 @@ MainWindow::MainWindow(QWidget* parent) :
m_menuObjects = menuBar()->addMenu(Locale::tr("qtapp.mainmenu:objects"));
menu = m_menuObjects->addMenu(Theme::getIcon("hardware"), Locale::tr("qtapp.mainmenu:hardware"));
menu->addAction(Locale::tr("world:command_stations") + "...", [this](){ showObject("world.command_stations", Locale::tr("world:command_stations")); });
menu->addAction(Locale::tr("world:interfaces") + "...", [this](){ showObject("world.interfaces", Locale::tr("world:interfaces")); });
menu->addAction(Locale::tr("world:decoders") + "...", [this](){ showObject("world.decoders", Locale::tr("world:decoders")); });
menu->addAction(Locale::tr("world:inputs") + "...", [this](){ showObject("world.inputs", Locale::tr("world:inputs")); });
menu->addAction(Locale::tr("world:outputs") + "...", [this](){ showObject("world.outputs", Locale::tr("world:outputs")); });
menu->addAction(Locale::tr("world:controllers") + "...", [this](){ showObject("world.controllers", Locale::tr("world:controllers")); });
m_menuObjects->addAction(Locale::tr("world:boards") + "...", [this](){ showObject("world.boards", Locale::tr("world:boards")); });
m_menuObjects->addAction(Locale::tr("world:clock") + "...", [this](){ showObject("world.clock", Locale::tr("world:clock")); });
m_menuObjects->addAction(Locale::tr("world:trains") + "...", [this](){ showObject("world.trains", Locale::tr("world:trains")); });

Datei anzeigen

@ -476,9 +476,9 @@ ObjectPtr Connection::readObject(const Message& message)
else
{
const QString classId = QString::fromLatin1(message.read<QByteArray>());
if(classId.startsWith(InputMonitor::classIdPrefix))
if(classId == InputMonitor::classId)
p = new InputMonitor(shared_from_this(), handle, classId);
else if(classId.startsWith(OutputKeyboard::classIdPrefix))
else if(classId == OutputKeyboard::classId)
p = new OutputKeyboard(shared_from_this(), handle, classId);
else if(classId.startsWith(OutputMap::classIdPrefix))
p = new OutputMap(shared_from_this(), handle, classId);

Datei anzeigen

@ -34,7 +34,7 @@ class InputMonitor final : public Object
int m_requestId;
public:
inline static const QString classIdPrefix = QStringLiteral("input_monitor.");
inline static const QString classId = QStringLiteral("input_monitor");
InputMonitor(const std::shared_ptr<Connection>& connection, Handle handle, const QString& classId);
~InputMonitor() final;

Datei anzeigen

@ -34,7 +34,7 @@ class OutputKeyboard final : public Object
int m_requestId;
public:
inline static const QString classIdPrefix = QStringLiteral("output_keyboard.");
inline static const QString classId = QStringLiteral("output_keyboard");
OutputKeyboard(std::shared_ptr<Connection> connection, Handle handle, const QString& classId);
~OutputKeyboard() final;

Datei anzeigen

@ -42,8 +42,6 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
return new ObjectListWidget(object, parent); // todo remove
else if(classId == "decoder_list")
return new ThrottleObjectListWidget(object, parent); // todo remove
else if(classId == "input_list")
return new ObjectListWidget(object, parent); // todo remove
else if(classId == "controller_list")
return new ObjectListWidget(object, parent); // todo remove
else if(classId == "rail_vehicle_list")
@ -52,6 +50,8 @@ QWidget* createWidgetIfCustom(const ObjectPtr& object, QWidget* parent)
return new ObjectListWidget(object, parent); // todo remove
else if(classId == "world_list")
return new ObjectListWidget(object, parent);
else if(object->classId().startsWith("list."))
return new ObjectListWidget(object, parent);
else if(classId == "lua.script")
return new LuaScriptEditWidget(object, parent);
else if(auto outputMap = std::dynamic_pointer_cast<OutputMap>(object))
@ -66,8 +66,6 @@ QWidget* createWidget(const ObjectPtr& object, QWidget* parent)
{
if(QWidget* widget = createWidgetIfCustom(object, parent))
return widget;
else if(object->classId().startsWith("list."))
return new ObjectListWidget(object, parent);
else if(auto inputMonitor = std::dynamic_pointer_cast<InputMonitor>(object))
return new InputMonitorWidget(inputMonitor, parent);
else if(auto outputKeyboard = std::dynamic_pointer_cast<OutputKeyboard>(object))

Datei anzeigen

@ -49,12 +49,16 @@
ObjectListWidget::ObjectListWidget(const ObjectPtr& object, QWidget* parent) :
QWidget(parent),
m_requestIdInputMonitor{Connection::invalidRequestId},
m_requestIdOutputKeyboard{Connection::invalidRequestId},
m_buttonAdd{nullptr},
m_object{object},
m_toolbar{new QToolBar()},
m_actionAdd{nullptr},
m_actionEdit{nullptr},
m_actionDelete{nullptr},
m_actionInputMonitor{nullptr},
m_actionOutputKeyboard{nullptr},
m_tableWidget{new TableWidget()}
{
m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -188,6 +192,43 @@ ObjectListWidget::ObjectListWidget(const ObjectPtr& object, QWidget* parent) :
m_actionDelete->setForceDisabled(true);
m_toolbar->addAction(m_actionDelete);
}
if(Method* method = m_object->getMethod("input_monitor"))
{
m_actionInputMonitor = new MethodAction(Theme::getIcon("input_monitor"), *method,
[this]()
{
m_requestIdInputMonitor = m_actionInputMonitor->method().call(
[this](const ObjectPtr& inputMonitor, Message::ErrorCode)
{
if(inputMonitor)
MainWindow::instance->showObject(inputMonitor);
});
});
}
if(Method* method = m_object->getMethod("output_keyboard"))
{
m_actionOutputKeyboard = new MethodAction(Theme::getIcon("output_keyboard"), *method,
[this]()
{
m_requestIdOutputKeyboard = m_actionOutputKeyboard->method().call(
[this](const ObjectPtr& outputKeyboard, Message::ErrorCode)
{
if(outputKeyboard)
MainWindow::instance->showObject(outputKeyboard);
});
});
}
if(m_actionInputMonitor || m_actionOutputKeyboard)
{
m_toolbar->addSeparator();
if(m_actionInputMonitor)
m_toolbar->addAction(m_actionInputMonitor);
if(m_actionOutputKeyboard)
m_toolbar->addAction(m_actionOutputKeyboard);
}
}
ObjectListWidget::~ObjectListWidget()

Datei anzeigen

@ -36,15 +36,18 @@ class ObjectListWidget : public QWidget
Q_OBJECT
private:
//const QString m_id;
int m_requestId;
int m_requestIdAdd;
int m_requestIdInputMonitor;
int m_requestIdOutputKeyboard;
ObjectPtr m_object;
QToolBar* m_toolbar;
QToolButton* m_buttonAdd;
QAction* m_actionAdd;
QAction* m_actionEdit;
MethodAction* m_actionDelete;
MethodAction* m_actionInputMonitor;
MethodAction* m_actionOutputKeyboard;
TableWidget* m_tableWidget;
void tableSelectionChanged();

Datei anzeigen

@ -77,10 +77,6 @@ file(GLOB SOURCES
"src/core/*.hpp"
"src/core/*.cpp"
"src/enum/*.hpp"
"src/hardware/commandstation/*.hpp"
"src/hardware/commandstation/*.cpp"
"src/hardware/controller/*.hpp"
"src/hardware/controller/*.cpp"
"src/hardware/decoder/*.hpp"
"src/hardware/decoder/*.cpp"
"src/hardware/input/*.hpp"
@ -91,6 +87,8 @@ file(GLOB SOURCES
"src/hardware/input/map/*.cpp"
"src/hardware/input/monitor/*.hpp"
"src/hardware/input/monitor/*.cpp"
"src/hardware/interface/*.hpp"
"src/hardware/interface/*.cpp"
"src/hardware/output/*.hpp"
"src/hardware/output/*.cpp"
"src/hardware/output/keyboard/*.hpp"
@ -99,14 +97,16 @@ file(GLOB SOURCES
"src/hardware/output/list/*.cpp"
"src/hardware/output/map/*.hpp"
"src/hardware/output/map/*.cpp"
"src/hardware/protocol/dccplusplus/*.hpp"
"src/hardware/protocol/dccplusplus/*.cpp"
# "src/hardware/protocol/dccplusplus/*.hpp"
# "src/hardware/protocol/dccplusplus/*.cpp"
"src/hardware/protocol/loconet/*.hpp"
"src/hardware/protocol/loconet/*.cpp"
"src/hardware/protocol/xpressnet/*.hpp"
"src/hardware/protocol/xpressnet/*.cpp"
"src/hardware/protocol/z21/*.hpp"
"src/hardware/protocol/z21/*.cpp"
"src/hardware/protocol/loconet/iohandler/*.hpp"
"src/hardware/protocol/loconet/iohandler/*.cpp"
# "src/hardware/protocol/xpressnet/*.hpp"
# "src/hardware/protocol/xpressnet/*.cpp"
# "src/hardware/protocol/z21/*.hpp"
# "src/hardware/protocol/z21/*.cpp"
"src/log/*.hpp"
"src/log/*.cpp"
"src/train/*.hpp"

Datei anzeigen

@ -1,140 +0,0 @@
/**
* server/src/core/commandstationproperty.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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.
*/
#include "commandstationproperty.hpp"
#include "../hardware/commandstation/commandstation.hpp"
using T = CommandStation;
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags) :
AbstractObjectProperty(object, name, flags),
m_value{value}
{
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags) :
CommandStationProperty(object, name, std::shared_ptr<T>(), flags)
{
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags, OnSet onSet) :
CommandStationProperty(object, name, value, flags)
{
m_onSet = onSet;
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags, OnSet onSet) :
CommandStationProperty(object, name, std::shared_ptr<T>(), flags, onSet)
{
}
const std::shared_ptr<T>& CommandStationProperty::value() const
{
return m_value;
}
void CommandStationProperty::setValue(const std::shared_ptr<T>& value)
{
assert(isWriteable());
if(m_value == value)
return;
else if(!isWriteable())
throw not_writable_error();
else if(!m_onSet || m_onSet(value))
{
m_value = value;
changed();
}
else
throw invalid_value_error();
}
void CommandStationProperty::setValueInternal(const std::shared_ptr<T>& value)
{
if(m_value != value)
{
m_value = value;
changed();
}
}
const T* CommandStationProperty::operator ->() const
{
return m_value.get();
}
T* CommandStationProperty::operator ->()
{
return m_value.get();
}
const T& CommandStationProperty::operator *() const
{
return *m_value;
}
T& CommandStationProperty::operator *()
{
return *m_value;
}
CommandStationProperty::operator bool()
{
return m_value.operator bool();
}
CommandStationProperty& CommandStationProperty::operator =(const std::shared_ptr<T>& value)
{
setValue(value);
return *this;
}
ObjectPtr CommandStationProperty::toObject() const
{
return std::dynamic_pointer_cast<Object>(m_value);
}
void CommandStationProperty::fromObject(const ObjectPtr& value)
{
if(value)
{
if(std::shared_ptr<T> v = std::dynamic_pointer_cast<T>(value))
setValue(v);
else
throw conversion_error();
}
else
setValue(nullptr);
}
void CommandStationProperty::load(const ObjectPtr& value)
{
if(value)
{
if(std::shared_ptr<T> v = std::dynamic_pointer_cast<T>(value))
m_value = v;
else
throw conversion_error();
}
else
m_value.reset();
}

Datei anzeigen

@ -1,68 +0,0 @@
/**
* server/src/core/commandstationproperty.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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 TRAINTASTIC_SERVER_CORE_COMMANDSTATIONPROPERTY_HPP
#define TRAINTASTIC_SERVER_CORE_COMMANDSTATIONPROPERTY_HPP
#include "abstractobjectproperty.hpp"
#include <functional>
class CommandStation;
//! workaround for ObjectProperty<CommandStation>
class CommandStationProperty : public AbstractObjectProperty
{
public:
using OnSet = std::function<bool(const std::shared_ptr<CommandStation>& value)>;
protected:
std::shared_ptr<CommandStation> m_value;
OnSet m_onSet;
public:
CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<CommandStation>& value, PropertyFlags flags);
CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags);
CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<CommandStation>& value, PropertyFlags flags, OnSet onSet);
CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags, OnSet onSet);
const std::shared_ptr<CommandStation>& value() const;
void setValue(const std::shared_ptr<CommandStation>& value);
void setValueInternal(const std::shared_ptr<CommandStation>& value);
const CommandStation* operator ->() const;
CommandStation* operator ->();
const CommandStation& operator *() const;
CommandStation& operator *();
operator bool();
CommandStationProperty& operator =(const std::shared_ptr<CommandStation>& value);
ObjectPtr toObject() const final;
void fromObject(const ObjectPtr& value) final;
void load(const ObjectPtr& value) final;
};
#endif

Datei anzeigen

@ -0,0 +1,51 @@
/**
* server/src/core/controllerlist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_CORE_CONTROLLERLIST_HPP
#define TRAINTASTIC_SERVER_CORE_CONTROLLERLIST_HPP
#include "controllerlistbase.hpp"
template<class T>
class ControllerList : public ControllerListBase
{
public:
ControllerList(Object& _parent, const std::string& parentPropertyName)
: ControllerListBase(_parent, parentPropertyName)
{
}
inline void add(const std::shared_ptr<T>& controller)
{
if(ObjectPtr object = std::dynamic_pointer_cast<Object>(controller))
ControllerListBase::add(std::move(object));
else
assert(false);
}
inline void remove(const std::shared_ptr<T>& controller)
{
ControllerListBase::remove(std::dynamic_pointer_cast<Object>(controller));
}
};
#endif

Datei anzeigen

@ -0,0 +1,79 @@
/**
* server/src/core/controllerlistbase.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "controllerlistbase.hpp"
#include "controllerlistbasetablemodel.hpp"
ControllerListBase::ControllerListBase(Object& _parent, const std::string& parentPropertyName)
: AbstractObjectList(_parent, parentPropertyName)
, length{this, "length", 0, PropertyFlags::ReadOnly}
{
}
TableModelPtr ControllerListBase::getModel()
{
return std::make_shared<ControllerListBaseTableModel>(*this);
}
void ControllerListBase::add(ObjectPtr controller)
{
assert(controller);
m_propertyChanged.emplace(controller.get(), controller->propertyChanged.connect(
[this](BaseProperty& property)
{
if(!m_models.empty() && ControllerListBaseTableModel::isListedProperty(property.name()))
{
ObjectPtr obj = property.object().shared_from_this();
const uint32_t rows = static_cast<uint32_t>(m_items.size());
for(uint32_t row = 0; row < rows; row++)
if(m_items[row] == obj)
{
for(auto& model : m_models)
model->propertyChanged(property, row);
break;
}
}
}));
m_items.emplace_back(std::move(controller));
rowCountChanged();
}
void ControllerListBase::remove(const ObjectPtr& controller)
{
assert(controller);
auto it = std::find(m_items.begin(), m_items.end(), controller);
if(it != m_items.end())
{
m_propertyChanged.erase(controller.get());
m_items.erase(it);
rowCountChanged();
}
}
void ControllerListBase::rowCountChanged()
{
const auto size = m_items.size();
length.setValueInternal(static_cast<uint32_t>(size));
for(auto& model : m_models)
model->setRowCount(static_cast<uint32_t>(size));
}

Datei anzeigen

@ -0,0 +1,59 @@
/**
* server/src/core/controllerlistbase.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASE_HPP
#define TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASE_HPP
#include "abstractobjectlist.hpp"
#include "property.hpp"
class ControllerListBaseTableModel;
class ControllerListBase : public AbstractObjectList
{
friend class ControllerListBaseTableModel;
CLASS_ID("list.controller")
private:
std::vector<ObjectPtr> m_items;
std::unordered_map<Object*, boost::signals2::connection> m_propertyChanged;
std::vector<ControllerListBaseTableModel*> m_models;
void rowCountChanged();
protected:
std::vector<ObjectPtr> getItems() const final { return m_items; }
void setItems(const std::vector<ObjectPtr>& items) final { m_items = items; }
void add(ObjectPtr object);
void remove(const ObjectPtr& object);
public:
Property<uint32_t> length;
ControllerListBase(Object& _parent, const std::string& parentPropertyName);
TableModelPtr getModel() final;
};
#endif

Datei anzeigen

@ -0,0 +1,81 @@
/**
* server/src/core/controllerlistbasetablemodel.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "controllerlistbasetablemodel.hpp"
#include "controllerlistbase.hpp"
#include "../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
bool ControllerListBaseTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name";
}
ControllerListBaseTableModel::ControllerListBaseTableModel(ControllerListBase& list)
: TableModel()
, m_list{list.shared_ptr<ControllerListBase>()}
{
list.m_models.push_back(this);
setRowCount(static_cast<uint32_t>(list.m_items.size()));
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
});
}
std::string ControllerListBaseTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const Object& object = *m_list->m_items[row];
switch(column)
{
case columnId:
return object.getObjectId();
case columnName:
if(const auto* property = object.getProperty("name"))
return property->toString();
break;
default:
assert(false);
break;
}
}
return "";
}
void ControllerListBaseTableModel::propertyChanged(BaseProperty& property, uint32_t row)
{
if(property.name() == "id")
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
}

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/hardware/input/monitor/xpressnetinputmonitor.hpp
* server/src/core/controllerlistbasetablemodel.hpp
*
* This file is part of the traintastic source code.
*
@ -20,29 +20,30 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INPUT_MONITOR_XPRESSNETINPUTMONITOR_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_MONITOR_XPRESSNETINPUTMONITOR_HPP
#ifndef TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASETABLEMODEL_HPP
#define TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASETABLEMODEL_HPP
#include "inputmonitor.hpp"
#include "tablemodel.hpp"
namespace XpressNet {
class XpressNet;
}
class ControllerListBase;
class XpressNetInputMonitor final : public InputMonitor
class ControllerListBaseTableModel final : public TableModel
{
protected:
std::shared_ptr<XpressNet::XpressNet> m_xpressnet;
CLASS_ID("table_model.controller_list")
friend class ControllerListBase;
private:
std::shared_ptr<ControllerListBase> m_list;
void propertyChanged(BaseProperty& property, uint32_t row);
public:
CLASS_ID("input_monitor.xpressnet")
static bool isListedProperty(const std::string& name);
XpressNetInputMonitor(std::shared_ptr<XpressNet::XpressNet> xpressnet);
~XpressNetInputMonitor() final;
ControllerListBaseTableModel(ControllerListBase& list);
std::string getObjectId() const final { return ""; }
std::vector<InputInfo> getInputInfo() const final;
std::string getText(uint32_t column, uint32_t row) const final;
};
#endif

Datei anzeigen

@ -35,6 +35,8 @@ class ObjectListTableModel : public TableModel
std::shared_ptr<ObjectList<T>> m_list;
protected:
static constexpr uint32_t invalidColumn = std::numeric_limits<uint32_t>::max();
const T& getItem(uint32_t row) const { return *m_list->m_items[row]; }
//T& getItem(uint32_t row) { return *m_list.m_items[row]; }
virtual void propertyChanged(BaseProperty& property, uint32_t row) = 0;

Datei anzeigen

@ -49,7 +49,6 @@
#include "settings.hpp"
#include "../hardware/commandstation/commandstationlist.hpp"
#include "../hardware/decoder/decoderlist.hpp"
#include <iostream>
@ -563,13 +562,13 @@ void Session::writeObject(Message& message, const ObjectPtr& object)
if(auto* inputMonitor = dynamic_cast<InputMonitor*>(object.get()))
{
inputMonitor->inputIdChanged = std::bind(&Session::inputMonitorInputIdChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
inputMonitor->inputValueChanged = std::bind(&Session::inputMonitorInputValueChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
m_objectSignals.emplace(handle, inputMonitor->inputIdChanged.connect(std::bind(&Session::inputMonitorInputIdChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
m_objectSignals.emplace(handle, inputMonitor->inputValueChanged.connect(std::bind(&Session::inputMonitorInputValueChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
}
else if(auto* outputKeyboard = dynamic_cast<OutputKeyboard*>(object.get()))
{
outputKeyboard->outputIdChanged = std::bind(&Session::outputKeyboardOutputIdChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
outputKeyboard->outputValueChanged = std::bind(&Session::outputKeyboardOutputValueChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
m_objectSignals.emplace(handle, outputKeyboard->outputIdChanged.connect(std::bind(&Session::outputKeyboardOutputIdChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
m_objectSignals.emplace(handle, outputKeyboard->outputValueChanged.connect(std::bind(&Session::outputKeyboardOutputValueChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
}
else if(auto* board = dynamic_cast<Board*>(object.get()))
{

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/output/outputs.cpp
* server/src/enum/interfacestatus.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,12 +20,16 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "outputs.hpp"
#ifndef TRAINTASTIC_SERVER_ENUM_INTERFACESTATUS_HPP
#define TRAINTASTIC_SERVER_ENUM_INTERFACESTATUS_HPP
std::shared_ptr<Output> Outputs::create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id)
{
if(classId == LocoNetOutput::classId)
return LocoNetOutput::create(world, id);
else
return std::shared_ptr<Output>();
}
#include <traintastic/enum/interfacestatus.hpp>
inline constexpr std::array<InterfaceStatus, 4> interfaceStatusValues{{
InterfaceStatus::Offline,
InterfaceStatus::Initializing,
InterfaceStatus::Online,
InterfaceStatus::Error,
}};
#endif

Datei anzeigen

@ -26,41 +26,33 @@
#include "decoderchangeflags.hpp"
#include "decoderfunction.hpp"
#include "decoderfunctions.hpp"
#include "../protocol/dcc/dcc.hpp"
#include "../../world/world.hpp"
#include "../commandstation/commandstation.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/almostzero.hpp"
//constexpr uint16_t addressDCCMin = 1;
constexpr uint16_t addressDCCShortMax = 127;
const std::shared_ptr<Decoder> Decoder::null;
Decoder::Decoder(const std::weak_ptr<World>& world, std::string_view _id) :
IdObject(world, _id),
name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
commandStation{this, "command_station", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<CommandStation>& value)
interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const std::shared_ptr<DecoderController>& newValue)
{
std::shared_ptr<Decoder> decoder = std::dynamic_pointer_cast<Decoder>(shared_from_this());
assert(decoder);
//if(value)
// TODO: check compatible??
if(commandStation)
commandStation->decoders->removeObject(decoder);
if(value)
value->decoders->addObject(decoder);
return true;
if(!newValue || newValue->addDecoder(*this))
{
if(interface.value())
interface->removeDecoder(*this);
return true;
}
return false;
}},
protocol{this, "protocol", DecoderProtocol::Auto, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const DecoderProtocol& value)
{
if(value == DecoderProtocol::DCC && address > addressDCCShortMax)
if(value == DecoderProtocol::DCC && DCC::isLongAddress(address))
longAddress = true;
updateEditable();
}},
@ -69,7 +61,7 @@ Decoder::Decoder(const std::weak_ptr<World>& world, std::string_view _id) :
{
if(protocol == DecoderProtocol::DCC)
{
if(value > addressDCCShortMax)
if(DCC::isLongAddress(value))
longAddress = true;
updateEditable();
}
@ -103,20 +95,19 @@ Decoder::Decoder(const std::weak_ptr<World>& world, std::string_view _id) :
functions.setValueInternal(std::make_shared<DecoderFunctions>(*this, functions.name()));
auto w = world.lock();
assert(w);
m_worldMute = contains(w->state.value(), WorldState::Mute);
m_worldNoSmoke = contains(w->state.value(), WorldState::NoSmoke);
// const bool editable = w && contains(w->state.value(), WorldState::Edit) && speedStep == 0;
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, false);
m_interfaceItems.add(name);
Attributes::addDisplayName(commandStation, DisplayName::Hardware::commandStation);
Attributes::addEnabled(commandStation, false);
Attributes::addObjectList(commandStation, w->commandStations);
m_interfaceItems.add(commandStation);
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
Attributes::addEnabled(interface, false);
Attributes::addObjectList(interface, w->decoderControllers);
m_interfaceItems.add(interface);
Attributes::addEnabled(protocol, false);
Attributes::addValues(protocol, DecoderProtocolValues);
@ -156,6 +147,20 @@ void Decoder::addToWorld()
world->decoders->addObject(shared_ptr<Decoder>());
}
void Decoder::loaded()
{
IdObject::loaded();
if(interface)
{
if(!interface->addDecoder(*this))
{
if(auto object = std::dynamic_pointer_cast<Object>(interface.value()))
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *object);
interface.setValueInternal(nullptr);
}
}
}
bool Decoder::hasFunction(uint32_t number) const
{
for(auto& f : *functions)
@ -220,8 +225,8 @@ void Decoder::setFunctionValue(uint32_t number, bool value)
void Decoder::destroying()
{
if(commandStation.value())
commandStation = nullptr;
if(interface.value())
interface = nullptr;
if(auto world = m_world.lock())
world->decoders->removeObject(shared_ptr<Decoder>());
IdObject::destroying();
@ -273,15 +278,15 @@ void Decoder::updateEditable(bool editable)
{
const bool stopped = editable && almostZero(throttle.value());
Attributes::setEnabled(name, editable);
Attributes::setEnabled(commandStation, stopped);
Attributes::setEnabled(interface, stopped);
Attributes::setEnabled(protocol, stopped);
Attributes::setEnabled(address, stopped);
Attributes::setEnabled(longAddress, stopped && protocol == DecoderProtocol::DCC && address < addressDCCShortMax);
Attributes::setEnabled(longAddress, stopped && protocol == DecoderProtocol::DCC && !DCC::isLongAddress(address));
Attributes::setEnabled(speedSteps, stopped);
}
void Decoder::changed(DecoderChangeFlags changes, uint32_t functionNumber)
{
if(commandStation)
commandStation->decoderChanged(*this, changes, functionNumber);
if(interface)
interface->decoderChanged(*this, changes, functionNumber);
}

Datei anzeigen

@ -26,9 +26,9 @@
#include <type_traits>
#include "../../core/idobject.hpp"
#include "../../core/objectproperty.hpp"
#include "../../core/commandstationproperty.hpp"
#include "../../enum/decoderprotocol.hpp"
#include "../../enum/direction.hpp"
#include "decodercontroller.hpp"
#include "decoderfunctions.hpp"
enum class DecoderChangeFlags;
@ -43,6 +43,7 @@ class Decoder : public IdObject
bool m_worldNoSmoke;
protected:
void loaded() final;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) final;
void updateEditable();
@ -74,7 +75,7 @@ class Decoder : public IdObject
static const std::shared_ptr<Decoder> null;
Property<std::string> name;
CommandStationProperty commandStation;
ObjectProperty<DecoderController> interface;
Property<DecoderProtocol> protocol;
Property<uint16_t> address;
Property<bool> longAddress;

Datei anzeigen

@ -0,0 +1,83 @@
/**
* server/src/hardware/decoder/decodercontroller.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "decodercontroller.hpp"
#include "decoder.hpp"
bool DecoderController::addDecoder(Decoder& decoder)
{
if(decoder.protocol != DecoderProtocol::Auto && findDecoder(decoder) != m_decoders.end())
return false;
else if(findDecoder(DecoderProtocol::Auto, decoder.address) != m_decoders.end())
return false;
m_decoders.emplace_back(decoder.shared_ptr<Decoder>());
return true;
}
bool DecoderController::removeDecoder(Decoder& decoder)
{
auto it = findDecoder(decoder);
if(it != m_decoders.end())
{
m_decoders.erase(it);
return true;
}
else
return false;
}
const std::shared_ptr<Decoder>& DecoderController::getDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress, bool fallbackToProtocolAuto)
{
auto it = findDecoder(protocol, address, dccLongAddress);
if(it != m_decoders.end())
return *it;
else if(fallbackToProtocolAuto && protocol != DecoderProtocol::Auto && (it = findDecoder(DecoderProtocol::Auto, address)) != m_decoders.end())
return *it;
else
return Decoder::null;
}
DecoderController::DecoderVector::iterator DecoderController::findDecoder(const Decoder& decoder)
{
return findDecoder(decoder.protocol, decoder.address, decoder.longAddress);
}
DecoderController::DecoderVector::iterator DecoderController::findDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress)
{
if(protocol == DecoderProtocol::DCC)
{
return std::find_if(m_decoders.begin(), m_decoders.end(),
[address, dccLongAddress](const auto& it)
{
return it->protocol == DecoderProtocol::DCC && it->address == address && it->longAddress == dccLongAddress;
});
}
else
{
return std::find_if(m_decoders.begin(), m_decoders.end(),
[protocol, address](const auto& it)
{
return it->protocol == protocol && it->address == address;
});
}
}

Datei anzeigen

@ -0,0 +1,54 @@
/**
* server/src/hardware/decoder/decodercontroller.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_DECODER_DECODERCONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_DECODER_DECODERCONTROLLER_HPP
#include <cstdint>
#include <vector>
#include <memory>
class Decoder;
enum class DecoderChangeFlags;
enum class DecoderProtocol : uint8_t;
class DecoderController
{
public:
using DecoderVector = std::vector<std::shared_ptr<Decoder>>;
protected:
DecoderVector m_decoders;
DecoderVector::iterator findDecoder(const Decoder& decoder);
DecoderVector::iterator findDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress = false);
public:
[[nodiscard]] virtual bool addDecoder(Decoder& decoder);
[[nodiscard]] virtual bool removeDecoder(Decoder& decoder);
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress = false, bool fallbackToProtocolAuto = false);
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) = 0;
};
#endif

Datei anzeigen

@ -22,7 +22,6 @@
#include "decoderlist.hpp"
#include "decoderlisttablemodel.hpp"
#include "../commandstation/commandstation.hpp"
#include "../../world/getworld.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
@ -35,12 +34,13 @@ DecoderList::DecoderList(Object& _parent, const std::string& parentPropertyName)
auto world = getWorld(&this->parent());
if(!world)
return std::shared_ptr<Decoder>();
auto decoder = Decoder::create(world, world->getUniqueId("decoder"));
//addObject(decoder);
if(auto* cs = dynamic_cast<CommandStation*>(&this->parent()))
decoder->commandStation = cs->shared_ptr<CommandStation>();
//else if(world->commandStations->length() == 1)
// decoder->commandStation = cs->shared_ptr<CommandStation>();
if(const auto controller = std::dynamic_pointer_cast<DecoderController>(parent().shared_from_this()))
{
// todo: select free address?
decoder->interface = controller;
}
return decoder;
}}
, remove{*this, "remove",

Datei anzeigen

@ -24,12 +24,31 @@
#include "../../world/world.hpp"
#include "list/inputlisttablemodel.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
Input::Input(const std::weak_ptr<World> world, std::string_view _id) :
IdObject(world, _id),
name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store},
value{this, "value", TriState::Undefined, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
Input::Input(const std::weak_ptr<World> world, std::string_view _id)
: IdObject(world, _id)
, name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store}
, interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const std::shared_ptr<InputController>& newValue)
{
if(!newValue || newValue->addInput(*this))
{
if(interface.value())
interface->removeInput(*this);
return true;
}
return false;
}}
, address{this, "address", 1, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint32_t& newValue)
{
if(interface)
return interface->changeInputAddress(*this, newValue);
return true;
}}
, value{this, "value", TriState::Undefined, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, consumers{*this, "consumers", {}, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
auto w = world.lock();
@ -38,9 +57,21 @@ Input::Input(const std::weak_ptr<World> world, std::string_view _id) :
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, editable);
m_interfaceItems.add(name);
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
Attributes::addEnabled(interface, editable);
Attributes::addObjectList(interface, w->inputControllers);
m_interfaceItems.add(interface);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, std::numeric_limits<uint32_t>::min(), std::numeric_limits<uint32_t>::max());
m_interfaceItems.add(address);
Attributes::addObjectEditor(value, false);
Attributes::addValues(value, TriStateValues);
m_interfaceItems.add(value);
Attributes::addObjectEditor(consumers, false); //! \todo add client support first
m_interfaceItems.add(consumers);
}
@ -53,8 +84,24 @@ void Input::addToWorld()
world->inputs->addObject(shared_ptr<Input>());
}
void Input::loaded()
{
IdObject::loaded();
if(interface)
{
if(!interface->addInput(*this))
{
if(auto object = std::dynamic_pointer_cast<Object>(interface.value()))
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *object);
interface.setValueInternal(nullptr);
}
}
}
void Input::destroying()
{
if(interface.value())
interface = nullptr;
if(auto world = m_world.lock())
world->inputs->removeObject(shared_ptr<Input>());
IdObject::destroying();
@ -67,6 +114,8 @@ void Input::worldEvent(WorldState state, WorldEvent event)
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(name, editable);
Attributes::setEnabled(interface, editable);
Attributes::setEnabled(address, editable);
}
void Input::updateValue(TriState _value)

Datei anzeigen

@ -27,13 +27,19 @@
#include "../../core/objectproperty.hpp"
#include "../../core/objectvectorproperty.hpp"
#include "../../enum/tristate.hpp"
#include "inputcontroller.hpp"
class Input : public IdObject
{
CLASS_ID("input")
DEFAULT_ID("input")
CREATE(Input)
friend class InputController;
protected:
void addToWorld() override;
void loaded() override;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
virtual void valueChanged(TriState /*_value*/) {}
@ -41,7 +47,11 @@ class Input : public IdObject
void updateValue(TriState _value);
public:
static constexpr uint32_t invalidAddress = std::numeric_limits<uint32_t>::max();
Property<std::string> name;
ObjectProperty<InputController> interface;
Property<uint32_t> address;
Property<TriState> value;
ObjectVectorProperty<Object> consumers;

Datei anzeigen

@ -0,0 +1,103 @@
/**
* server/src/hardware/input/inputcontroller.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "inputcontroller.hpp"
#include "input.hpp"
#include "monitor/inputmonitor.hpp"
#include "../../utils/inrange.hpp"
bool InputController::isInputAddressAvailable(uint32_t address) const
{
return
inRange(address, inputAddressMinMax()) &&
m_inputs.find(address) == m_inputs.end();
}
uint32_t InputController::getUnusedInputAddress() const
{
const auto end = m_inputs.cend();
const auto range = inputAddressMinMax();
for(uint32_t address = range.first; address < range.second; address++)
if(m_inputs.find(address) == end)
return address;
return Input::invalidAddress;
}
bool InputController::changeInputAddress(Input& input, uint32_t newAddress)
{
assert(input.interface.value().get() == this);
if(!isInputAddressAvailable(newAddress))
return false;
auto node = m_inputs.extract(input.address); // old address
node.key() = newAddress;
m_inputs.insert(std::move(node));
input.value.setValueInternal(TriState::Undefined);
return true;
}
bool InputController::addInput(Input& input)
{
if(isInputAddressAvailable(input.address))
{
m_inputs.insert({input.address, input.shared_ptr<Input>()});
input.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
bool InputController::removeInput(Input& input)
{
assert(input.interface.value().get() == this);
auto it = m_inputs.find(input.address);
if(it != m_inputs.end() && it->second.get() == &input)
{
m_inputs.erase(it);
input.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
void InputController::updateInputValue(uint32_t address, TriState value)
{
if(auto it = m_inputs.find(address); it != m_inputs.end())
it->second->updateValue(value);
if(auto monitor = m_inputMonitor.lock())
monitor->inputValueChanged(*monitor, address, value);
}
std::shared_ptr<InputMonitor> InputController::inputMonitor()
{
auto monitor = m_inputMonitor.lock();
if(!monitor)
{
monitor = std::make_shared<InputMonitor>(*this);
m_inputMonitor = monitor;
}
return monitor;
}

Datei anzeigen

@ -0,0 +1,101 @@
/**
* server/src/hardware/input/inputcontroller.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTCONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTCONTROLLER_HPP
#include <cstdint>
#include <unordered_map>
#include <memory>
#include "../../enum/tristate.hpp"
class Input;
class InputMonitor;
class InputController
{
public:
using InputMap = std::unordered_map<uint32_t, std::shared_ptr<Input>>;
protected:
InputMap m_inputs;
std::weak_ptr<InputMonitor> m_inputMonitor;
public:
/**
*
*/
inline const InputMap& inputs() const { return m_inputs; }
/**
*
*/
virtual std::pair<uint32_t, uint32_t> inputAddressMinMax() const = 0;
/**
*
*/
[[nodiscard]] virtual bool isInputAddressAvailable(uint32_t address) const;
/**
* @brief Get the next unused input address
*
* @return An usused address or #Input::invalidAddress if no unused address is available.
*/
uint32_t getUnusedInputAddress() const;
/**
*
* @return \c true if changed, \c false otherwise.
*/
[[nodiscard]] virtual bool changeInputAddress(Input& input, uint32_t newAddress);
/**
*
* @return \c true if added, \c false otherwise.
*/
[[nodiscard]] virtual bool addInput(Input& input);
/**
*
* @return \c true if removed, \c false otherwise.
*/
[[nodiscard]] virtual bool removeInput(Input& input);
/**
* @brief Update the input value
*
* This function should be called by the hardware layer whenever the input value changes.
*
* @param[in] address Input address
* @param[in] value New input value
*/
void updateInputValue(uint32_t address, TriState value);
/**
*
*
*/
std::shared_ptr<InputMonitor> inputMonitor();
};
#endif

Datei anzeigen

@ -22,59 +22,64 @@
#include "inputlist.hpp"
#include "inputlisttablemodel.hpp"
#include "../inputs.hpp"
#include "../inputcontroller.hpp"
#include "../../../world/getworld.hpp"
#include "../../../core/attributes.hpp"
#include "../../../utils/displayname.hpp"
InputList::InputList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<Input>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view inputClassId)
{
auto world = getWorld(&this->parent());
if(!world)
return std::shared_ptr<Input>();
auto input = Inputs::create(world, inputClassId, world->getUniqueId("input"));
if(auto locoNetInput = std::dynamic_pointer_cast<LocoNetInput>(input); locoNetInput && world->loconets->length == 1)
InputList::InputList(Object& _parent, const std::string& parentPropertyName)
: ObjectList<Input>(_parent, parentPropertyName)
, m_parentIsInputController{dynamic_cast<InputController*>(&_parent)}
, add{*this, "add",
[this]()
{
auto& loconet = world->loconets->operator[](0);
if(uint16_t address = loconet->getUnusedInputAddress(); address != LocoNetInput::addressInvalid)
auto world = getWorld(&parent());
if(!world)
return std::shared_ptr<Input>();
auto input = Input::create(world, world->getUniqueId(Input::defaultId));
if(const auto controller = std::dynamic_pointer_cast<InputController>(parent().shared_from_this()))
{
locoNetInput->address = address;
locoNetInput->loconet = loconet;
if(const uint32_t address = controller->getUnusedInputAddress(); address != Input::invalidAddress)
{
input->address = address;
input->interface = controller;
}
}
}
else if(auto xpressNetInput = std::dynamic_pointer_cast<XpressNetInput>(input); xpressNetInput && world->xpressnets->length == 1)
{
auto& xpressnet = world->xpressnets->operator[](0);
if(uint16_t address = xpressnet->getUnusedInputAddress(); address != XpressNetInput::addressInvalid)
{
xpressNetInput->address = address;
xpressNetInput->xpressnet = xpressnet;
}
}
return input;
}}
return input;
}}
, remove{*this, "remove",
[this](const std::shared_ptr<Input>& input)
{
if(containsObject(input))
input->destroy();
assert(!containsObject(input));
}}
[this](const std::shared_ptr<Input>& input)
{
if(containsObject(input))
input->destroy();
assert(!containsObject(input));
}}
, inputMonitor{*this, "input_monitor",
[this]()
{
if(const auto controller = std::dynamic_pointer_cast<InputController>(parent().shared_from_this()))
return controller->inputMonitor();
else
return std::shared_ptr<InputMonitor>();
}}
{
auto w = getWorld(&_parent);
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(add, DisplayName::List::add);
Attributes::addEnabled(add, editable);
Attributes::addClassList(add, Inputs::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
Attributes::addEnabled(remove, editable);
m_interfaceItems.add(remove);
if(m_parentIsInputController)
{
Attributes::addDisplayName(inputMonitor, DisplayName::Hardware::inputMonitor);
m_interfaceItems.add(inputMonitor);
}
}
TableModelPtr InputList::getModel()

Datei anzeigen

@ -26,21 +26,28 @@
#include "../../../core/objectlist.hpp"
#include "../../../core/method.hpp"
#include "../input.hpp"
#include "../monitor/inputmonitor.hpp"
class InputList : public ObjectList<Input>
{
CLASS_ID("list.input")
private:
const bool m_parentIsInputController;
protected:
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("input_list")
Method<std::shared_ptr<Input>(std::string_view)> add;
Method<std::shared_ptr<Input>()> add;
Method<void(const std::shared_ptr<Input>&)> remove;
Method<std::shared_ptr<InputMonitor>()> inputMonitor;
InputList(Object& _parent, const std::string& parentPropertyName);
inline bool parentIsInputController() const { return m_parentIsInputController; }
TableModelPtr getModel() final;
};

Datei anzeigen

@ -22,30 +22,39 @@
#include "inputlisttablemodel.hpp"
#include "inputlist.hpp"
#include "../loconetinput.hpp"
#include "../../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnBus = 2;
constexpr uint32_t columnAddress = 3;
bool InputListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name";
name == "name" ||
name == "interface" ||
name == "address";
}
InputListTableModel::InputListTableModel(InputList& list) :
ObjectListTableModel<Input>(list)
InputListTableModel::InputListTableModel(InputList& list)
: ObjectListTableModel<Input>(list)
, m_columnInterface(list.parentIsInputController() ? invalidColumn : 2)
, m_columnAddress(list.parentIsInputController() ? 2 : 3)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
"input_list:bus",
DisplayName::Hardware::address,
});
if(list.parentIsInputController())
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Hardware::address,
});
}
else
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Hardware::interface,
DisplayName::Hardware::address,
});
}
}
std::string InputListTableModel::getText(uint32_t column, uint32_t row) const
@ -53,32 +62,27 @@ std::string InputListTableModel::getText(uint32_t column, uint32_t row) const
if(row < rowCount())
{
const Input& input = getItem(row);
const LocoNetInput* inputLocoNet = dynamic_cast<const LocoNetInput*>(&input);
switch(column)
if(column == columnId)
return input.id;
else if(column == columnName)
return input.name;
else if(column == m_columnInterface)
{
case columnId:
return input.id;
case columnName:
return input.name;
case columnBus: // virtual method @ Input ??
if(inputLocoNet && inputLocoNet->loconet)
return inputLocoNet->loconet->getObjectId();
if(const auto& interface = std::dynamic_pointer_cast<Object>(input.interface.value()))
{
if(auto property = interface->getProperty("name"); property && !property->toString().empty())
return property->toString();
else
return interface->getObjectId();
}
else
return "";
case columnAddress: // virtual method @ Input ??
if(inputLocoNet)
return std::to_string(inputLocoNet->address);
else
return "";
default:
assert(false);
break;
}
else if(column == m_columnAddress)
return std::to_string(input.address.value());
else
assert(false);
}
return "";
@ -90,4 +94,8 @@ void InputListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
}
else if(property.name() == "interface" && m_columnInterface != invalidColumn)
changed(row, m_columnInterface);
else if(property.name() == "address")
changed(row, m_columnAddress);
}

Datei anzeigen

@ -32,6 +32,12 @@ class InputListTableModel : public ObjectListTableModel<Input>
{
friend class InputList;
private:
static constexpr uint32_t columnId = 0;
static constexpr uint32_t columnName = 1;
const uint32_t m_columnInterface;
const uint32_t m_columnAddress;
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;

Datei anzeigen

@ -1,104 +0,0 @@
/**
* server/src/hardware/input/loconetinput.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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.
*/
#include "loconetinput.hpp"
#include "../../core/attributes.hpp"
#include "../../world/world.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetInput::LocoNetInput(const std::weak_ptr<World> world, std::string_view _id) :
Input(world, _id),
loconet{this, "loconet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<LocoNet::LocoNet>& newValue)
{
if(!newValue || newValue->addInput(*this))
{
if(loconet.value())
loconet->removeInput(*this);
return true;
}
return false;
}},
address{this, "address", addressMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint16_t& newValue)
{
if(loconet)
return loconet->changeInputAddress(*this, newValue);
return true;
}}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
Attributes::addEnabled(loconet, editable);
Attributes::addObjectList(loconet, w->loconets);
m_interfaceItems.add(loconet);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMin, addressMax);
m_interfaceItems.add(address);
}
void LocoNetInput::loaded()
{
Input::loaded();
if(loconet)
{
if(!loconet->addInput(*this))
{
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *loconet);
loconet.setValueInternal(nullptr);
}
}
}
void LocoNetInput::destroying()
{
if(loconet)
loconet = nullptr;
Input::destroying();
}
void LocoNetInput::worldEvent(WorldState state, WorldEvent event)
{
Input::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(loconet, editable);
Attributes::setEnabled(address, editable);
}
void LocoNetInput::idChanged(const std::string& newId)
{
if(loconet)
loconet->inputMonitorIdChanged(address, newId);
}
void LocoNetInput::valueChanged(TriState _value)
{
if(loconet)
loconet->inputMonitorValueChanged(address, _value);
}

Datei anzeigen

@ -1,57 +0,0 @@
/**
* server/src/hardware/input/loconetinput.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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 TRAINTASTIC_SERVER_HARDWARE_INPUT_LOCONETINPUT_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_LOCONETINPUT_HPP
#include "input.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet/loconet.hpp"
class LocoNetInput : public Input
{
friend class LocoNet::LocoNet;
protected:
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void valueChanged(TriState _value) final;
inline void updateValue(TriState _value) { Input::updateValue(_value); }
public:
CLASS_ID("input.loconet")
CREATE(LocoNetInput)
static constexpr uint16_t addressInvalid = 0;
static constexpr uint16_t addressMin = 1;
static constexpr uint16_t addressMax = 4096;
ObjectProperty<LocoNet::LocoNet> loconet;
Property<uint16_t> address;
LocoNetInput(const std::weak_ptr<World> world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/hardware/input/monitor/loconetinputmonitor.cpp
* server/src/hardware/input/monitor/inputmonitor.cpp
*
* This file is part of the traintastic source code.
*
@ -20,31 +20,29 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "loconetinputmonitor.hpp"
#include "../../protocol/loconet/loconet.hpp"
#include "../loconetinput.hpp"
#include "inputmonitor.hpp"
#include "../inputcontroller.hpp"
#include "../input.hpp"
LocoNetInputMonitor::LocoNetInputMonitor(std::shared_ptr<LocoNet::LocoNet> loconet) :
InputMonitor(),
m_loconet{std::move(loconet)}
InputMonitor::InputMonitor(InputController& controller)
: Object()
, m_controller{controller}
, addressMin{this, "address_min", m_controller.inputAddressMinMax().first, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, addressMax{this, "address_max", m_controller.inputAddressMinMax().second, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
addressMin.setValueInternal(LocoNetInput::addressMin);
addressMax.setValueInternal(LocoNetInput::addressMax);
m_loconet->m_inputMonitors.emplace_back(this);
}
LocoNetInputMonitor::~LocoNetInputMonitor()
std::string InputMonitor::getObjectId() const
{
if(auto it = std::find(m_loconet->m_inputMonitors.begin(), m_loconet->m_inputMonitors.end(), this); it != m_loconet->m_inputMonitors.end())
m_loconet->m_inputMonitors.erase(it);
return ""; // todo
}
std::vector<InputMonitor::InputInfo> LocoNetInputMonitor::getInputInfo() const
std::vector<InputMonitor::InputInfo> InputMonitor::getInputInfo() const
{
std::vector<InputInfo> inputInfo;
for(auto it : m_loconet->m_inputs)
for(auto it : m_controller.inputs())
{
LocoNetInput& input = *(it.second);
const auto& input = *(it.second);
InputInfo info(input.address, input.id, input.value);
inputInfo.push_back(info);
}

Datei anzeigen

@ -28,11 +28,18 @@
#include "../../../core/property.hpp"
#include "../../../enum/tristate.hpp"
class InputController;
class InputMonitor : public Object
{
CLASS_ID("input_monitor")
private:
InputController& m_controller;
public:
std::function<void(InputMonitor&, uint32_t, std::string_view)> inputIdChanged;
std::function<void(InputMonitor&, uint32_t, TriState)> inputValueChanged;
boost::signals2::signal<void(InputMonitor&, uint32_t, std::string_view)> inputIdChanged;
boost::signals2::signal<void(InputMonitor&, uint32_t, TriState)> inputValueChanged;
struct InputInfo
{
@ -51,14 +58,11 @@ class InputMonitor : public Object
Property<uint32_t> addressMin;
Property<uint32_t> addressMax;
InputMonitor() :
Object(),
addressMin{this, "address_min", 0, PropertyFlags::ReadOnly | PropertyFlags::NoStore},
addressMax{this, "address_max", 0, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
}
InputMonitor(InputController& controller);
virtual std::vector<InputInfo> getInputInfo() const = 0;
std::string getObjectId() const final;
virtual std::vector<InputInfo> getInputInfo() const;
};
#endif

Datei anzeigen

@ -1,52 +0,0 @@
/**
* server/src/hardware/input/monitor/xpressnetinputmonitor.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "xpressnetinputmonitor.hpp"
#include "../../protocol/xpressnet/xpressnet.hpp"
#include "../xpressnetinput.hpp"
XpressNetInputMonitor::XpressNetInputMonitor(std::shared_ptr<XpressNet::XpressNet> xpressnet) :
InputMonitor(),
m_xpressnet{std::move(xpressnet)}
{
addressMin.setValueInternal(XpressNetInput::addressMin);
addressMax.setValueInternal(XpressNetInput::addressMax);
m_xpressnet->m_inputMonitors.emplace_back(this);
}
XpressNetInputMonitor::~XpressNetInputMonitor()
{
if(auto it = std::find(m_xpressnet->m_inputMonitors.begin(), m_xpressnet->m_inputMonitors.end(), this); it != m_xpressnet->m_inputMonitors.end())
m_xpressnet->m_inputMonitors.erase(it);
}
std::vector<InputMonitor::InputInfo> XpressNetInputMonitor::getInputInfo() const
{
std::vector<InputInfo> inputInfo;
for(auto it : m_xpressnet->m_inputs)
{
XpressNetInput& input = *(it.second);
InputInfo info(input.address, input.id, input.value);
inputInfo.push_back(info);
}
return inputInfo;
}

Datei anzeigen

@ -1,104 +0,0 @@
/**
* server/src/hardware/input/xpressnetinput.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "xpressnetinput.hpp"
#include "../../core/attributes.hpp"
#include "../../world/world.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
XpressNetInput::XpressNetInput(const std::weak_ptr<World> world, std::string_view _id) :
Input(world, _id),
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<XpressNet::XpressNet>& newValue)
{
if(!newValue || newValue->addInput(*this))
{
if(xpressnet.value())
xpressnet->removeInput(*this);
return true;
}
return false;
}},
address{this, "address", addressMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint16_t& newValue)
{
if(xpressnet)
return xpressnet->changeInputAddress(*this, newValue);
return true;
}}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(xpressnet, DisplayName::Hardware::xpressnet);
Attributes::addEnabled(xpressnet, editable);
Attributes::addObjectList(xpressnet, w->xpressnets);
m_interfaceItems.add(xpressnet);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMin, addressMax);
m_interfaceItems.add(address);
}
void XpressNetInput::loaded()
{
Input::loaded();
if(xpressnet)
{
if(!xpressnet->addInput(*this))
{
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *xpressnet);
xpressnet.setValueInternal(nullptr);
}
}
}
void XpressNetInput::destroying()
{
if(xpressnet)
xpressnet = nullptr;
Input::destroying();
}
void XpressNetInput::worldEvent(WorldState state, WorldEvent event)
{
Input::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(xpressnet, editable);
Attributes::setEnabled(address, editable);
}
void XpressNetInput::idChanged(const std::string& newId)
{
if(xpressnet)
xpressnet->inputMonitorIdChanged(address, newId);
}
void XpressNetInput::valueChanged(TriState _value)
{
if(xpressnet)
xpressnet->inputMonitorValueChanged(address, _value);
}

Datei anzeigen

@ -1,57 +0,0 @@
/**
* server/src/hardware/input/xpressnetinput.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_INPUT_XPRESSNETINPUT_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_XPRESSNETINPUT_HPP
#include "input.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
class XpressNetInput : public Input
{
friend class XpressNet::XpressNet;
protected:
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void valueChanged(TriState _value) final;
inline void updateValue(TriState _value) { Input::updateValue(_value); }
public:
CLASS_ID("input.xpressnet")
CREATE(XpressNetInput)
static constexpr uint16_t addressInvalid = 0;
static constexpr uint16_t addressMin = 1;
static constexpr uint16_t addressMax = 2048;
ObjectProperty<XpressNet::XpressNet> xpressnet;
Property<uint16_t> address;
XpressNetInput(const std::weak_ptr<World> world, std::string_view _id);
};
#endif

Datei anzeigen

@ -0,0 +1,88 @@
/**
* server/src/hardware/interface/interface.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "interface.hpp"
#include "interfacelisttablemodel.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
#include "../../world/world.hpp"
Interface::Interface(const std::weak_ptr<World>& world, std::string_view _id)
: IdObject(world, _id)
, name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, online{this, "online", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore, nullptr, std::bind(&Interface::setOnline, this, std::placeholders::_1)}
, status{this, "status", InterfaceStatus::Offline, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, editable);
m_interfaceItems.add(name);
Attributes::addDisplayName(online, DisplayName::CommandStation::online);
m_interfaceItems.add(online);
Attributes::addDisplayName(status, DisplayName::Interface::status);
Attributes::addValues(status, interfaceStatusValues);
m_interfaceItems.add(status);
Attributes::addDisplayName(notes, DisplayName::Object::notes);
m_interfaceItems.add(notes);
}
void Interface::addToWorld()
{
IdObject::addToWorld();
if(auto world = m_world.lock())
world->interfaces->addObject(shared_ptr<Interface>());
}
void Interface::destroying()
{
if(auto world = m_world.lock())
world->interfaces->removeObject(shared_ptr<Interface>());
IdObject::destroying();
}
void Interface::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
Attributes::setEnabled(name, contains(state, WorldState::Edit));
switch(event)
{
case WorldEvent::Offline:
online = false;
break;
case WorldEvent::Online:
online = true;
break;
default:
break;
}
}

Datei anzeigen

@ -0,0 +1,50 @@
/**
* server/src/hardware/interface/interface.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACE_HPP
#include "../../core/idobject.hpp"
#include "../../enum/interfacestatus.hpp"
/**
* @brief Base class for a hardware interface
*/
class Interface : public IdObject
{
protected:
Interface(const std::weak_ptr<World>& world, std::string_view _id);
void addToWorld() override;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
virtual bool setOnline(bool& value) = 0;
public:
Property<std::string> name;
Property<bool> online;
Property<InterfaceStatus> status;
Property<std::string> notes;
};
#endif

Datei anzeigen

@ -0,0 +1,80 @@
/**
* server/src/hardware/interface/interfacelist.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "interfacelist.hpp"
#include "interfacelisttablemodel.hpp"
#include "interfaces.hpp"
#include "../../world/world.hpp"
#include "../../world/getworld.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
InterfaceList::InterfaceList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<Interface>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view interfaceClassId)
{
auto world = getWorld(this);
if(!world)
return std::shared_ptr<Interface>();
return Interfaces::create(world, interfaceClassId);
}},
remove{*this, "remove",
[this](const std::shared_ptr<Interface>& object)
{
if(containsObject(object))
object->destroy();
assert(!containsObject(object));
}}
{
auto world = getWorld(&_parent);
const bool editable = world && contains(world->state.value(), WorldState::Edit);
Attributes::addDisplayName(add, DisplayName::List::add);
Attributes::addEnabled(add, editable);
Attributes::addClassList(add, Interfaces::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
Attributes::addEnabled(remove, editable);
m_interfaceItems.add(remove);
}
TableModelPtr InterfaceList::getModel()
{
return std::make_shared<InterfaceListTableModel>(*this);
}
void InterfaceList::worldEvent(WorldState state, WorldEvent event)
{
ObjectList<Interface>::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(add, editable);
Attributes::setEnabled(remove, editable);
}
bool InterfaceList::isListedProperty(const std::string& name)
{
return InterfaceListTableModel::isListedProperty(name);
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/output/keyboard/loconetoutputkeyboard.hpp
* server/src/hardware/interface/interfacelist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,30 +20,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_OUTPUT_KEYBOARD_LOCONETOUTPUTKEYBOARD_HPP
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_KEYBOARD_LOCONETOUTPUTKEYBOARD_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELIST_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELIST_HPP
#include "outputkeyboard.hpp"
#include "../../core/objectlist.hpp"
#include "../../core/method.hpp"
#include "interface.hpp"
namespace LocoNet {
class LocoNet;
}
class LocoNetOutputKeyboard final : public OutputKeyboard
class InterfaceList : public ObjectList<Interface>
{
protected:
std::shared_ptr<LocoNet::LocoNet> m_loconet;
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("output_keyboard.loconet")
CLASS_ID("list.interface")
LocoNetOutputKeyboard(std::shared_ptr<LocoNet::LocoNet> loconet);
~LocoNetOutputKeyboard() final;
Method<std::shared_ptr<Interface>(std::string_view)> add;
Method<void(const std::shared_ptr<Interface>&)> remove;
std::string getObjectId() const final { return ""; }
InterfaceList(Object& _parent, const std::string& parentPropertyName);
std::vector<OutputInfo> getOutputInfo() const final;
void setOutputValue(uint32_t address, bool value) final;
TableModelPtr getModel() final;
};
#endif

Datei anzeigen

@ -0,0 +1,83 @@
/**
* server/src/hardware/interface/interfacelisttablemodel.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "interfacelisttablemodel.hpp"
#include "interfacelist.hpp"
#include "../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnStatus = 2;
bool InterfaceListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name" ||
name == "status";
}
InterfaceListTableModel::InterfaceListTableModel(InterfaceList& list) :
ObjectListTableModel<Interface>(list)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Interface::status,
});
}
std::string InterfaceListTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const Interface& interface = getItem(row);
switch(column)
{
case columnId:
return interface.id;
case columnName:
return interface.name;
case columnStatus:
return "<todo>";
default:
assert(false);
break;
}
}
return "";
}
void InterfaceListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
{
if(property.name() == "id")
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
else if(property.name() == "status")
changed(row, columnStatus);
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/input/monitor/loconetinputmonitor.hpp
* server/src/hardware/interface/interfacelisttablemodel.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,29 +20,29 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INPUT_MONITOR_LOCONETINPUTMONITOR_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_MONITOR_LOCONETINPUTMONITOR_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELISTTABLEMODEL_HPP
#include "inputmonitor.hpp"
#include "../../core/objectlisttablemodel.hpp"
#include "interfacelist.hpp"
namespace LocoNet {
class LocoNet;
}
class InterfaceList;
class LocoNetInputMonitor final : public InputMonitor
class InterfaceListTableModel : public ObjectListTableModel<Interface>
{
friend class InterfaceList;
protected:
std::shared_ptr<LocoNet::LocoNet> m_loconet;
void propertyChanged(BaseProperty& property, uint32_t row) final;
public:
CLASS_ID("input_monitor.loconet")
CLASS_ID("table_model.list.interface")
LocoNetInputMonitor(std::shared_ptr<LocoNet::LocoNet> loconet);
~LocoNetInputMonitor() final;
static bool isListedProperty(const std::string& name);
std::string getObjectId() const final { return ""; }
InterfaceListTableModel(InterfaceList& interfaceList);
std::vector<InputInfo> getInputInfo() const final;
std::string getText(uint32_t column, uint32_t row) const final;
};
#endif

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/input/inputs.cpp
* server/src/hardware/interface/interfaces.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,13 +20,11 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "inputs.hpp"
#include "interfaces.hpp"
#include "../../utils/ifclassidcreate.hpp"
#include "../../world/world.hpp"
std::shared_ptr<Input> Inputs::create(const std::shared_ptr<World>& world, std::string_view classId, std::string_view id)
std::shared_ptr<Interface> Interfaces::create(const std::shared_ptr<World>& world, std::string_view classId, std::string_view id)
{
IF_CLASSID_CREATE(LocoNetInput)
IF_CLASSID_CREATE(XpressNetInput)
return std::shared_ptr<Input>();
return std::shared_ptr<Interface>();
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/output/outputs.hpp
* server/src/hardware/interface/interfaces.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,23 +20,21 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_OUTPUT_OUTPUTS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_OUTPUTS_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACES_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACES_HPP
#include "output.hpp"
#include "interface.hpp"
#include "../../utils/makearray.hpp"
#include "loconetoutput.hpp"
struct Outputs
struct Interfaces
{
static constexpr std::string_view classIdPrefix = "output.";
static constexpr std::string_view classIdPrefix = "interface.";
static constexpr auto classList = makeArray(
LocoNetOutput::classId
);
static std::shared_ptr<Output> create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id);
static std::shared_ptr<Interface> create(const std::shared_ptr<World>& world, std::string_view classId, std::string_view id);
};
#endif

Datei anzeigen

@ -1,64 +0,0 @@
/**
* server/src/hardware/output/keyboard/loconetoutputkeyboard.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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.
*/
#include "loconetoutputkeyboard.hpp"
#include "../../protocol/loconet/loconet.hpp"
#include "../loconetoutput.hpp"
LocoNetOutputKeyboard::LocoNetOutputKeyboard(std::shared_ptr<LocoNet::LocoNet> loconet) :
OutputKeyboard(),
m_loconet{std::move(loconet)}
{
addressMin.setValueInternal(LocoNetOutput::addressMin);
addressMax.setValueInternal(LocoNetOutput::addressMax);
m_loconet->m_outputKeyboards.emplace_back(this);
}
LocoNetOutputKeyboard::~LocoNetOutputKeyboard()
{
if(auto it = std::find(m_loconet->m_outputKeyboards.begin(), m_loconet->m_outputKeyboards.end(), this); it != m_loconet->m_outputKeyboards.end())
m_loconet->m_outputKeyboards.erase(it);
}
std::vector<OutputKeyboard::OutputInfo> LocoNetOutputKeyboard::getOutputInfo() const
{
std::vector<OutputInfo> outputInfo;
for(auto it : m_loconet->m_outputs)
{
LocoNetOutput& output = *(it.second);
OutputInfo info(output.address, output.id, output.value);
outputInfo.push_back(info);
}
return outputInfo;
}
void LocoNetOutputKeyboard::setOutputValue(uint32_t address, bool value)
{
if(address < LocoNetOutput::addressMin || address > LocoNetOutput::addressMax)
return;
auto it = m_loconet->m_outputs.find(address);
if(it != m_loconet->m_outputs.end())
it->second->value = toTriState(value);
else
m_loconet->send(LocoNet::SwitchRequest(address - 1, value));
}

Datei anzeigen

@ -0,0 +1,55 @@
/**
* server/src/hardware/output/keyboard/outputkeyboard.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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.
*/
#include "outputkeyboard.hpp"
#include "../output.hpp"
#include "../outputcontroller.hpp"
#include "../../../utils/inrange.hpp"
OutputKeyboard::OutputKeyboard(OutputController& controller)
: Object()
, m_controller{controller}
, addressMin{this, "address_min", m_controller.outputAddressMinMax().first, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, addressMax{this, "address_max", m_controller.outputAddressMinMax().second, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
}
std::string OutputKeyboard::getObjectId() const
{
return "";
}
std::vector<OutputKeyboard::OutputInfo> OutputKeyboard::getOutputInfo() const
{
std::vector<OutputInfo> outputInfo;
for(auto it : m_controller.outputs())
{
const auto& output = *(it.second);
outputInfo.emplace_back(OutputInfo{output.address, output.id, output.value});
}
return outputInfo;
}
void OutputKeyboard::setOutputValue(uint32_t address, bool value)
{
m_controller.setOutputValue(address, value);
}

Datei anzeigen

@ -28,11 +28,18 @@
#include "../../../core/property.hpp"
#include "../../../enum/tristate.hpp"
class OutputController;
class OutputKeyboard : public Object
{
CLASS_ID("output_keyboard")
private:
OutputController& m_controller;
public:
std::function<void(OutputKeyboard&, uint32_t, std::string_view)> outputIdChanged;
std::function<void(OutputKeyboard&, uint32_t, TriState)> outputValueChanged;
boost::signals2::signal<void(OutputKeyboard&, uint32_t, std::string_view)> outputIdChanged;
boost::signals2::signal<void(OutputKeyboard&, uint32_t, TriState)> outputValueChanged;
struct OutputInfo
{
@ -51,15 +58,12 @@ class OutputKeyboard : public Object
Property<uint32_t> addressMin;
Property<uint32_t> addressMax;
OutputKeyboard() :
Object(),
addressMin{this, "address_min", 0, PropertyFlags::ReadOnly | PropertyFlags::NoStore},
addressMax{this, "address_max", 0, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
}
OutputKeyboard(OutputController& controller);
virtual std::vector<OutputInfo> getOutputInfo() const = 0;
virtual void setOutputValue(uint32_t address, bool value) = 0;
std::string getObjectId() const final;
std::vector<OutputInfo> getOutputInfo() const;
void setOutputValue(uint32_t address, bool value);
};
#endif

Datei anzeigen

@ -22,31 +22,32 @@
#include "outputlist.hpp"
#include "outputlisttablemodel.hpp"
#include "../outputs.hpp"
#include "../outputcontroller.hpp"
#include "../../../world/getworld.hpp"
#include "../../../core/attributes.hpp"
#include "../../../utils/displayname.hpp"
OutputList::OutputList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<Output>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view outputClassId)
{
auto world = getWorld(&this->parent());
if(!world)
return std::shared_ptr<Output>();
auto output = Outputs::create(world, outputClassId, world->getUniqueId("output"));
if(auto locoNetOutput = std::dynamic_pointer_cast<LocoNetOutput>(output); locoNetOutput && world->loconets->length == 1)
OutputList::OutputList(Object& _parent, const std::string& parentPropertyName)
: ObjectList<Output>(_parent, parentPropertyName)
, m_parentIsOutputController{dynamic_cast<OutputController*>(&_parent)}
, add{*this, "add",
[this]()
{
auto& loconet = world->loconets->operator[](0);
if(uint16_t address = loconet->getUnusedOutputAddress(); address != LocoNetOutput::addressInvalid)
auto world = getWorld(&parent());
if(!world)
return std::shared_ptr<Output>();
auto output = Output::create(world, world->getUniqueId(Output::defaultId));
if(const auto controller = std::dynamic_pointer_cast<OutputController>(parent().shared_from_this()))
{
locoNetOutput->address = address;
locoNetOutput->loconet = loconet;
if(const uint32_t address = controller->getUnusedOutputAddress(); address != Output::invalidAddress)
{
output->address = address;
output->interface = controller;
}
}
}
return output;
}}
return output;
}}
, remove{*this, "remove",
[this](const std::shared_ptr<Output>& output)
{
@ -54,18 +55,31 @@ OutputList::OutputList(Object& _parent, const std::string& parentPropertyName) :
output->destroy();
assert(!containsObject(output));
}}
, outputKeyboard{*this, "output_keyboard",
[this]()
{
if(const auto controller = std::dynamic_pointer_cast<OutputController>(parent().shared_from_this()))
return controller->outputKeyboard();
else
return std::shared_ptr<OutputKeyboard>();
}}
{
auto w = getWorld(&_parent);
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(add, DisplayName::List::add);
Attributes::addEnabled(add, editable);
Attributes::addClassList(add, Outputs::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
Attributes::addEnabled(remove, editable);
m_interfaceItems.add(remove);
if(m_parentIsOutputController)
{
Attributes::addDisplayName(outputKeyboard, DisplayName::Hardware::outputKeyboard);
m_interfaceItems.add(outputKeyboard);
}
}
TableModelPtr OutputList::getModel()

Datei anzeigen

@ -26,21 +26,28 @@
#include "../../../core/objectlist.hpp"
#include "../../../core/method.hpp"
#include "../output.hpp"
#include "../keyboard/outputkeyboard.hpp"
class OutputList : public ObjectList<Output>
{
CLASS_ID("list.output")
private:
const bool m_parentIsOutputController;
protected:
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("list.output")
Method<std::shared_ptr<Output>(std::string_view)> add;
Method<std::shared_ptr<Output>()> add;
Method<void(const std::shared_ptr<Output>&)> remove;
Method<std::shared_ptr<OutputKeyboard>()> outputKeyboard;
OutputList(Object& _parent, const std::string& parentPropertyName);
inline bool parentIsOutputController() const { return m_parentIsOutputController; }
TableModelPtr getModel() final;
};

Datei anzeigen

@ -22,30 +22,39 @@
#include "outputlisttablemodel.hpp"
#include "outputlist.hpp"
#include "../loconetoutput.hpp"
#include "../../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnBus = 2;
constexpr uint32_t columnAddress = 3;
bool OutputListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name";
name == "name" ||
name == "interface" ||
name == "address";
}
OutputListTableModel::OutputListTableModel(OutputList& list) :
ObjectListTableModel<Output>(list)
, m_columnInterface(list.parentIsOutputController() ? invalidColumn : 2)
, m_columnAddress(list.parentIsOutputController() ? 2 : 3)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
"output_list:bus",
DisplayName::Hardware::address,
});
if(list.parentIsOutputController())
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Hardware::address,
});
}
else
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Hardware::interface,
DisplayName::Hardware::address,
});
}
}
std::string OutputListTableModel::getText(uint32_t column, uint32_t row) const
@ -53,32 +62,27 @@ std::string OutputListTableModel::getText(uint32_t column, uint32_t row) const
if(row < rowCount())
{
const Output& output = getItem(row);
const LocoNetOutput* outputLocoNet = dynamic_cast<const LocoNetOutput*>(&output);
switch(column)
if(column == columnId)
return output.id;
else if(column == columnName)
return output.name;
else if(column == m_columnInterface)
{
case columnId:
return output.id;
case columnName:
return output.name;
case columnBus: // virtual method @ Output ??
if(outputLocoNet && outputLocoNet->loconet)
return outputLocoNet->loconet->getObjectId();
if(const auto& interface = std::dynamic_pointer_cast<Object>(output.interface.value()))
{
if(auto property = interface->getProperty("name"); property && !property->toString().empty())
return property->toString();
else
return interface->getObjectId();
}
else
return "";
case columnAddress: // virtual method @ Output ??
if(outputLocoNet)
return std::to_string(outputLocoNet->address);
else
return "";
default:
assert(false);
break;
}
else if(column == m_columnAddress)
return std::to_string(output.address.value());
else
assert(false);
}
return "";
@ -90,4 +94,8 @@ void OutputListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
else if(property.name() == "interface" && m_columnInterface != invalidColumn)
changed(row, m_columnInterface);
else if(property.name() == "address")
changed(row, m_columnAddress);
}

Datei anzeigen

@ -32,6 +32,12 @@ class OutputListTableModel : public ObjectListTableModel<Output>
{
friend class OutputList;
private:
static constexpr uint32_t columnId = 0;
static constexpr uint32_t columnName = 1;
const uint32_t m_columnInterface;
const uint32_t m_columnAddress;
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;

Datei anzeigen

@ -1,112 +0,0 @@
/**
* server/src/hardware/output/loconetoutput.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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.
*/
#include "loconetoutput.hpp"
#include "../../core/attributes.hpp"
#include "../../world/world.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetOutput::LocoNetOutput(const std::weak_ptr<World> world, std::string_view _id) :
Output(world, _id),
loconet{this, "loconet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<LocoNet::LocoNet>& newValue)
{
if(!newValue || newValue->addOutput(*this))
{
if(loconet.value())
loconet->removeOutput(*this);
return true;
}
return false;
}},
address{this, "address", addressMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint16_t& newValue)
{
if(loconet)
return loconet->changeOutputAddress(*this, newValue);
return true;
}}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
Attributes::addEnabled(loconet, editable);
Attributes::addObjectList(loconet, w->loconets);
m_interfaceItems.add(loconet);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMin, addressMax);
m_interfaceItems.add(address);
}
void LocoNetOutput::loaded()
{
Output::loaded();
if(loconet)
{
if(!loconet->addOutput(*this))
{
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *loconet);
loconet.setValueInternal(nullptr);
}
}
}
void LocoNetOutput::destroying()
{
if(loconet)
loconet = nullptr;
Output::destroying();
}
void LocoNetOutput::worldEvent(WorldState state, WorldEvent event)
{
Output::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(loconet, editable);
Attributes::setEnabled(address, editable);
}
void LocoNetOutput::idChanged(const std::string& newId)
{
if(loconet)
loconet->outputKeyboardIdChanged(address, newId);
}
void LocoNetOutput::valueChanged(TriState _value)
{
if(loconet)
loconet->outputKeyboardValueChanged(address, _value);
}
bool LocoNetOutput::setValue(TriState& _value)
{
if(!loconet || _value == TriState::Undefined)
return false;
else
return loconet->send(LocoNet::SwitchRequest(address - 1, _value == TriState::True));
}

Datei anzeigen

@ -1,58 +0,0 @@
/**
* server/src/hardware/output/loconetoutput.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 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 TRAINTASTIC_SERVER_HARDWARE_OUTPUT_LOCONETOUTPUT_HPP
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_LOCONETOUTPUT_HPP
#include "output.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet/loconet.hpp"
class LocoNetOutput : public Output
{
friend class LocoNet::LocoNet;
protected:
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void valueChanged(TriState _value) final;
bool setValue(TriState& _value) final;
inline void updateValue(TriState _value) { Output::updateValue(_value); }
public:
CLASS_ID("output.loconet")
CREATE(LocoNetOutput)
static constexpr uint16_t addressInvalid = 0;
static constexpr uint16_t addressMin = 1;
static constexpr uint16_t addressMax = 4096;
ObjectProperty<LocoNet::LocoNet> loconet;
Property<uint16_t> address;
LocoNetOutput(const std::weak_ptr<World> world, std::string_view _id);
};
#endif

Datei anzeigen

@ -24,20 +24,55 @@
#include "../../world/world.hpp"
#include "list/outputlisttablemodel.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
Output::Output(const std::weak_ptr<World> world, std::string_view _id) :
IdObject(world, _id),
name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store},
value{this, "value", TriState::Undefined, PropertyFlags::ReadWrite | PropertyFlags::StoreState,
[this](TriState newValue)
{
valueChanged(newValue);
},
[this](TriState& newValue) -> bool
{
return setValue(newValue);
}}
Output::Output(const std::weak_ptr<World> world, std::string_view _id)
: IdObject(world, _id)
, name{this, "name", id, PropertyFlags::ReadWrite | PropertyFlags::Store}
, interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<OutputController>& newValue)
{
if(newValue)
{
const auto limits = newValue->outputAddressMinMax();
Attributes::setMinMax(address, limits.first, limits.second);
}
else
{
Attributes::setMinMax(address, addressMinDefault, addressMaxDefault);
value.setValueInternal(TriState::Undefined);
}
},
[this](const std::shared_ptr<OutputController>& newValue)
{
if(!newValue || newValue->addOutput(*this))
{
if(interface.value())
interface->removeOutput(*this);
return true;
}
return false;
}}
, address{this, "address", 1, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint32_t& newValue)
{
if(interface)
return interface->changeOutputAddress(*this, newValue);
return true;
}}
, value{this, "value", TriState::Undefined, PropertyFlags::ReadWrite | PropertyFlags::StoreState,
[this](TriState newValue)
{
valueChanged(newValue);
},
[this](TriState& newValue) -> bool
{
if(!interface || newValue == TriState::Undefined)
return false;
else
return interface->setOutputValue(address, newValue == TriState::True);
}}
, controllers{*this, "controllers", {}, PropertyFlags::ReadWrite | PropertyFlags::NoStore}
{
auto w = world.lock();
@ -46,9 +81,21 @@ Output::Output(const std::weak_ptr<World> world, std::string_view _id) :
Attributes::addDisplayName(name, DisplayName::Object::name);
Attributes::addEnabled(name, editable);
m_interfaceItems.add(name);
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
Attributes::addEnabled(interface, editable);
Attributes::addObjectList(interface, w->outputControllers);
m_interfaceItems.add(interface);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMinDefault, addressMaxDefault);
m_interfaceItems.add(address);
Attributes::addObjectEditor(value, false);
Attributes::addValues(value, TriStateValues);
m_interfaceItems.add(value);
Attributes::addObjectEditor(controllers, false); //! \todo add client support first
m_interfaceItems.add(controllers);
}
@ -61,10 +108,26 @@ void Output::addToWorld()
world->outputs->addObject(shared_ptr<Output>());
}
void Output::loaded()
{
IdObject::loaded();
if(interface)
{
if(!interface->addOutput(*this))
{
if(auto object = std::dynamic_pointer_cast<Object>(interface.value()))
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *object);
interface.setValueInternal(nullptr);
}
}
}
void Output::destroying()
{
if(auto world = m_world.lock())
world->outputs->removeObject(shared_ptr<Output>());
if(interface)
interface = nullptr;
IdObject::destroying();
}
@ -75,6 +138,8 @@ void Output::worldEvent(WorldState state, WorldEvent event)
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(name, editable);
Attributes::setEnabled(interface, editable);
Attributes::setEnabled(address, editable);
}
void Output::updateValue(TriState _value)

Datei anzeigen

@ -24,21 +24,38 @@
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_OUTPUT_HPP
#include "../../core/idobject.hpp"
#include "../../core/objectproperty.hpp"
#include "../../core/objectvectorproperty.hpp"
#include "../../enum/tristate.hpp"
#include "outputcontroller.hpp"
class Output : public IdObject
class Output final : public IdObject
{
CLASS_ID("output")
DEFAULT_ID("output")
CREATE(Output)
friend class OutputController;
private:
static constexpr uint32_t addressMinDefault = 0;
static constexpr uint32_t addressMaxDefault = 1'000'000;
protected:
void addToWorld() override;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
void addToWorld() final;
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
virtual void valueChanged(TriState /*_value*/) {}
virtual bool setValue(TriState& /*_value*/) { return true; }
void updateValue(TriState _value);
public:
static constexpr uint32_t invalidAddress = std::numeric_limits<uint32_t>::max();
Property<std::string> name;
ObjectProperty<OutputController> interface;
Property<uint32_t> address;
Property<TriState> value;
ObjectVectorProperty<Object> controllers;

Datei anzeigen

@ -0,0 +1,103 @@
/**
* server/src/hardware/output/outputcontroller.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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.
*/
#include "outputcontroller.hpp"
#include "output.hpp"
#include "keyboard/outputkeyboard.hpp"
#include "../../utils/inrange.hpp"
bool OutputController::isOutputAddressAvailable(uint32_t address) const
{
return
inRange(address, outputAddressMinMax()) &&
m_outputs.find(address) == m_outputs.end();
}
uint32_t OutputController::getUnusedOutputAddress() const
{
const auto end = m_outputs.cend();
const auto range = outputAddressMinMax();
for(uint32_t address = range.first; address < range.second; address++)
if(m_outputs.find(address) == end)
return address;
return Output::invalidAddress;
}
bool OutputController::changeOutputAddress(Output& output, uint32_t newAddress)
{
assert(output.interface.value().get() == this);
if(!isOutputAddressAvailable(newAddress))
return false;
auto node = m_outputs.extract(output.address); // old address
node.key() = newAddress;
m_outputs.insert(std::move(node));
output.value.setValueInternal(TriState::Undefined);
return true;
}
bool OutputController::addOutput(Output& output)
{
if(isOutputAddressAvailable(output.address))
{
m_outputs.insert({output.address, output.shared_ptr<Output>()});
output.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
bool OutputController::removeOutput(Output& output)
{
assert(output.interface.value().get() == this);
auto it = m_outputs.find(output.address);
if(it != m_outputs.end() && it->second.get() == &output)
{
m_outputs.erase(it);
output.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
void OutputController::updateOutputValue(uint32_t address, TriState value)
{
if(auto it = m_outputs.find(address); it != m_outputs.end())
it->second->updateValue(value);
if(auto keyboard = m_outputKeyboard.lock())
keyboard->outputValueChanged(*keyboard, address, value);
}
std::shared_ptr<OutputKeyboard> OutputController::outputKeyboard()
{
auto keyboard = m_outputKeyboard.lock();
if(!keyboard)
{
keyboard = std::make_shared<OutputKeyboard>(*this);
m_outputKeyboard = keyboard;
}
return keyboard;
}

Datei anzeigen

@ -0,0 +1,106 @@
/**
* server/src/hardware/output/outputcontroller.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_OUTPUT_OUTPUTCONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_OUTPUT_OUTPUTCONTROLLER_HPP
#include <cstdint>
#include <unordered_map>
#include <memory>
#include "../../enum/tristate.hpp"
class Output;
class OutputKeyboard;
class OutputController
{
public:
using OutputMap = std::unordered_map<uint32_t, std::shared_ptr<Output>>;
protected:
OutputMap m_outputs;
std::weak_ptr<OutputKeyboard> m_outputKeyboard;
public:
/**
*
*/
inline const OutputMap& outputs() const { return m_outputs; }
/**
*
*/
virtual std::pair<uint32_t, uint32_t> outputAddressMinMax() const = 0;
/**
*
*/
[[nodiscard]] virtual bool isOutputAddressAvailable(uint32_t address) const;
/**
* @brief Get the next unused output address
*
* @return An usused address or Output::invalidAddress if no unused address is available.
*/
uint32_t getUnusedOutputAddress() const;
/**
*
* @return \c true if changed, \c false otherwise.
*/
[[nodiscard]] virtual bool changeOutputAddress(Output& output, uint32_t newAddress);
/**
*
* @return \c true if added, \c false otherwise.
*/
[[nodiscard]] virtual bool addOutput(Output& output);
/**
*
* @return \c true if removed, \c false otherwise.
*/
[[nodiscard]] virtual bool removeOutput(Output& output);
/**
* @brief ...
*/
[[nodiscard]] virtual bool setOutputValue(uint32_t address, bool value) = 0;
/**
* @brief Update the output value
*
* This function should be called by the hardware layer whenever the output value changes.
*
* @param[in] address Output address
* @param[in] value New output value
*/
void updateOutputValue(uint32_t address, TriState value);
/**
*
*
*/
std::shared_ptr<OutputKeyboard> outputKeyboard();
};
#endif

Datei anzeigen

@ -0,0 +1,44 @@
/**
* server/src/hardware/protocol/dcc/dcc.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_DCC_DCC_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_DCC_DCC_HPP
#include <cstdint>
#include "../../../utils/inrange.hpp"
namespace DCC {
constexpr uint16_t addressBroadcast = 0;
constexpr uint16_t addressMin = 1;
constexpr uint16_t addressShortMax = 127;
constexpr uint16_t addressLongStart = 128;
constexpr uint16_t addressLongMax = 10239;
constexpr bool isLongAddress(uint16_t address)
{
return inRange(address, addressLongStart, addressLongMax);
}
}
#endif

Datei anzeigen

@ -54,30 +54,6 @@
#include "../clock/clock.hpp"
#include "../hardware/controller/wlanmaus.hpp"
#include "../hardware/controller/controllerlist.hpp"
#ifdef USB_XPRESSNET
#include "../hardware/controller/usbxpressnetcontroller.hpp"
#endif
#include "../hardware/commandstation/rocoz21.hpp"
#include "../hardware/commandstation/virtualcommandstation.hpp"
#include "../hardware/commandstation/loconettcpbinary.hpp"
#ifdef USB_XPRESSNET
#include "../hardware/commandstation/usbxpressnetinterface.hpp"
#endif
#include "../hardware/commandstation/xpressnetserial.hpp"
#include "../hardware/commandstation/dccplusplusserial.hpp"
#include "../hardware/commandstation/commandstationlist.hpp"
#include "../hardware/commandstation/loconetserial.hpp"
#include "../hardware/protocol/loconet/loconetlist.hpp"
#include "../hardware/protocol/loconet/loconet.hpp"
#include "../hardware/protocol/loconet/loconetlisttablemodel.hpp"
#include "../hardware/protocol/xpressnet/xpressnetlist.hpp"
#include "../hardware/protocol/xpressnet/xpressnetlisttablemodel.hpp"
#include "../hardware/protocol/xpressnet/xpressnet.hpp"
#include "../hardware/protocol/dccplusplus/dccplusplus.hpp"
#include "../hardware/decoder/decoderfunction.hpp"
#include "../hardware/decoder/decoderlist.hpp"
@ -85,24 +61,18 @@
#include "../hardware/decoder/decoderfunctions.hpp"
#include "../hardware/decoder/decoderlisttablemodel.hpp"
#include "../hardware/input/loconetinput.hpp"
#include "../hardware/input/monitor/xpressnetinputmonitor.hpp"
#include "../hardware/input/monitor/loconetinputmonitor.hpp"
#include "../hardware/input/xpressnetinput.hpp"
#include "../hardware/input/input.hpp"
#include "../hardware/input/list/inputlist.hpp"
#include "../hardware/input/list/inputlisttablemodel.hpp"
#include "../hardware/input/map/blockinputmap.hpp"
#include "../hardware/input/map/blockinputmapitem.hpp"
#include "../hardware/output/output.hpp"
#include "../hardware/output/list/outputlist.hpp"
#include "../hardware/output/list/outputlisttablemodel.hpp"
#include "../hardware/output/keyboard/loconetoutputkeyboard.hpp"
#include "../hardware/output/map/outputmapoutputaction.hpp"
#include "../hardware/output/map/signaloutputmap.hpp"
#include "../hardware/output/map/turnoutoutputmap.hpp"
#include "../hardware/output/map/turnoutoutputmapitem.hpp"
#include "../hardware/output/map/signaloutputmapitem.hpp"
#include "../hardware/output/loconetoutput.hpp"
#include "../vehicle/rail/railvehiclelist.hpp"
#include "../vehicle/rail/locomotive.hpp"
@ -170,34 +140,16 @@ void Class::registerValues(lua_State* L)
setField<Clock>(L, "CLOCK");
setField<WLANmaus>(L, "WLANMAUS_CONTROLLER");
setField<ControllerList>(L, "CONTROLLER_LIST");
#ifdef USB_XPRESSNET
setField<USBXpressNetController>(L, "USB_XPRESSNET_CONTROLLER");
#endif
setField<RocoZ21>(L, "Z21_COMMAND_STATION");
setField<VirtualCommandStation>(L, "VIRTUAL_COMMAND_STATION");
setField<LocoNetTCPBinary>(L, "LOCONET_TCP_BINARY_COMMAND_STATION");
#ifdef USB_XPRESSNET
setField<USBXpressNetInterface>(L, "USB_XPRESSNET_INTERFACE");
#endif
setField<XpressNetSerial>(L, "XPRESSNET_SERIAL_COMMAND_STATION");
setField<DCCPlusPlusSerial>(L, "DCCPLUSPLUS_SERIAL_COMMAND_STATION");
setField<CommandStationList>(L, "COMMAND_STATION_LIST");
setField<LocoNetSerial>(L, "LOCONET_SERIAL_COMMAND_STATION");
setField<DecoderFunction>(L, "DECODER_FUNCTION");
setField<DecoderList>(L, "DECODER_LIST");
setField<Decoder>(L, "DECODER");
setField<DecoderFunctions>(L, "DECODER_FUNCTIONS");
setField<LocoNetInput>(L, "LOCONET_INPUT");
setField<XpressNetInput>(L, "XPRESSNET_INPUT");
setField<Input>(L, "INPUT");
setField<InputList>(L, "INPUT_LIST");
setField<Output>(L, "OUTPUT");
setField<OutputList>(L, "OUTPUT_LIST");
setField<LocoNetOutput>(L, "LOCONET_OUTPUT");
setField<RailVehicleList>(L, "RAIL_VEHICLE_LIST");
setField<Locomotive>(L, "LOCOMOTIVE");

Datei anzeigen

@ -46,12 +46,20 @@ namespace DisplayName
{
constexpr std::string_view address = "hardware:address";
constexpr std::string_view commandStation = "hardware:command_station";
constexpr std::string_view decoders = "hardware:decoders";
constexpr std::string_view inputMonitor = "hardware:input_monitor";
constexpr std::string_view inputs = "hardware:inputs";
constexpr std::string_view interface = "hardware:interface";
constexpr std::string_view loconet = "hardware:loconet";
constexpr std::string_view outputKeyboard = "hardware:output_keyboard";
constexpr std::string_view outputs = "hardware:outputs";
constexpr std::string_view speedSteps = "hardware:speed_steps";
constexpr std::string_view xpressnet = "hardware:xpressnet";
}
namespace Interface
{
constexpr std::string_view status = "interface:status";
}
namespace IP
{
constexpr std::string_view hostname = "ip:hostname";
@ -72,9 +80,9 @@ namespace DisplayName
}
namespace Serial
{
constexpr std::string_view device = "serial:device";
constexpr std::string_view baudrate = "serial:baudrate";
constexpr std::string_view flowControl = "serial:flow_control";
constexpr std::string_view port = "serial:port";
}
namespace Vehicle
{

40
server/src/utils/inrange.hpp Normale Datei
Datei anzeigen

@ -0,0 +1,40 @@
/**
* server/src/utils/inrange.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 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 TRAINTASTIC_SERVER_UTILS_INRANGE_HPP
#define TRAINTASTIC_SERVER_UTILS_INRANGE_HPP
#include <utility>
template<class T>
constexpr bool inRange(const T value, const T min, const T max)
{
return value >= min && value <= max;
}
template<class T>
constexpr bool inRange(const T value, const std::pair<T, T>& range)
{
return inRange(value, range.first, range.second);
}
#endif

Datei anzeigen

@ -47,13 +47,14 @@ std::shared_ptr<World> World::create()
void World::init(const std::shared_ptr<World>& world)
{
world->commandStations.setValueInternal(std::make_shared<CommandStationList>(*world, world->commandStations.name()));
world->decoderControllers.setValueInternal(std::make_shared<ControllerList<DecoderController>>(*world, world->decoderControllers.name()));
world->inputControllers.setValueInternal(std::make_shared<ControllerList<InputController>>(*world, world->inputControllers.name()));
world->outputControllers.setValueInternal(std::make_shared<ControllerList<OutputController>>(*world, world->outputControllers.name()));
world->interfaces.setValueInternal(std::make_shared<InterfaceList>(*world, world->interfaces.name()));
world->decoders.setValueInternal(std::make_shared<DecoderList>(*world, world->decoders.name()));
world->inputs.setValueInternal(std::make_shared<InputList>(*world, world->inputs.name()));
world->outputs.setValueInternal(std::make_shared<OutputList>(*world, world->outputs.name()));
world->controllers.setValueInternal(std::make_shared<ControllerList>(*world, world->controllers.name()));
world->loconets.setValueInternal(std::make_shared<LocoNetList>(*world, world->loconets.name()));
world->xpressnets.setValueInternal(std::make_shared<XpressNetList>(*world, world->xpressnets.name()));
world->boards.setValueInternal(std::make_shared<BoardList>(*world, world->boards.name()));
world->clock.setValueInternal(std::make_shared<Clock>(*world, world->clock.name()));
world->trains.setValueInternal(std::make_shared<TrainList>(*world, world->trains.name()));
@ -69,13 +70,13 @@ World::World(Private) :
name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
scale{this, "scale", WorldScale::H0, PropertyFlags::ReadWrite | PropertyFlags::Store, [this](WorldScale /*value*/){ updateScaleRatio(); }},
scaleRatio{this, "scale_ratio", 87, PropertyFlags::ReadWrite | PropertyFlags::Store},
commandStations{this, "command_stations", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
decoderControllers{this, "input_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
inputControllers{this, "input_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
outputControllers{this, "output_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
interfaces{this, "interfaces", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
controllers{this, "controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
loconets{this, "loconets", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
xpressnets{this, "xpressnets", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
boards{this, "boards", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
clock{this, "clock", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
trains{this, "trains", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
@ -242,20 +243,21 @@ World::World(Private) :
Attributes::addVisible(scaleRatio, false);
m_interfaceItems.add(scaleRatio);
Attributes::addObjectEditor(commandStations, false);
m_interfaceItems.add(commandStations);
Attributes::addObjectEditor(decoderControllers, false);
m_interfaceItems.add(decoderControllers);
Attributes::addObjectEditor(inputControllers, false);
m_interfaceItems.add(inputControllers);
Attributes::addObjectEditor(outputControllers, false);
m_interfaceItems.add(outputControllers);
Attributes::addObjectEditor(interfaces, false);
m_interfaceItems.add(interfaces);
Attributes::addObjectEditor(decoders, false);
m_interfaceItems.add(decoders);
Attributes::addObjectEditor(inputs, false);
m_interfaceItems.add(inputs);
Attributes::addObjectEditor(outputs, false);
m_interfaceItems.add(outputs);
Attributes::addObjectEditor(controllers, false);
m_interfaceItems.add(controllers);
Attributes::addObjectEditor(loconets, false);
m_interfaceItems.add(loconets);
Attributes::addObjectEditor(xpressnets, false);
m_interfaceItems.add(xpressnets);
Attributes::addObjectEditor(boards, false);
m_interfaceItems.add(boards);
Attributes::addObjectEditor(clock, false);

Datei anzeigen

@ -26,6 +26,7 @@
#include "../core/object.hpp"
#include "../core/property.hpp"
#include "../core/objectproperty.hpp"
#include "../core/controllerlist.hpp"
#include <traintastic/utils/stdfilesystem.hpp>
#include <unordered_map>
#include <boost/uuid/uuid.hpp>
@ -34,13 +35,13 @@
#include <traintastic/set/worldstate.hpp>
#include "../clock/clock.hpp"
#include "../board/boardlist.hpp"
#include "../hardware/commandstation/commandstationlist.hpp"
#include "../hardware/interface/interfacelist.hpp"
#include "../hardware/decoder/decoderlist.hpp"
#include "../hardware/decoder/decodercontroller.hpp"
#include "../hardware/input/list/inputlist.hpp"
#include "../hardware/input/inputcontroller.hpp"
#include "../hardware/output/list/outputlist.hpp"
#include "../hardware/controller/controllerlist.hpp"
#include "../hardware/protocol/loconet/loconetlist.hpp"
#include "../hardware/protocol/xpressnet/xpressnetlist.hpp"
#include "../hardware/output/outputcontroller.hpp"
#include "../train/trainlist.hpp"
#include "../vehicle/rail/railvehiclelist.hpp"
#ifndef DISABLE_LUA_SCRIPTING
@ -85,13 +86,14 @@ class World : public Object
Property<WorldScale> scale;
Property<double> scaleRatio;
ObjectProperty<CommandStationList> commandStations;
ObjectProperty<ControllerList<DecoderController>> decoderControllers;
ObjectProperty<ControllerList<InputController>> inputControllers;
ObjectProperty<ControllerList<OutputController>> outputControllers;
ObjectProperty<InterfaceList> interfaces;
ObjectProperty<DecoderList> decoders;
ObjectProperty<InputList> inputs;
ObjectProperty<OutputList> outputs;
ObjectProperty<ControllerList> controllers;
ObjectProperty<LocoNetList> loconets;
ObjectProperty<XpressNetList> xpressnets;
ObjectProperty<BoardList> boards;
ObjectProperty<Clock> clock;
ObjectProperty<TrainList> trains;

Datei anzeigen

@ -31,12 +31,9 @@
#include "../board/board.hpp"
#include "../board/tile/tiles.hpp"
#include "../hardware/commandstation/commandstations.hpp"
#include "../hardware/controller/controllers.hpp"
#include "../hardware/interface/interfaces.hpp"
#include "../hardware/decoder/decoder.hpp"
#include "../hardware/decoder/decoderfunction.hpp"
#include "../hardware/input/inputs.hpp"
#include "../hardware/output/outputs.hpp"
#include "../vehicle/rail/railvehicles.hpp"
#include "../train/train.hpp"
#ifndef DISABLE_LUA_SCRIPTING
@ -154,10 +151,8 @@ void WorldLoader::createObject(ObjectData& objectData)
std::string_view classId = objectData.json["class_id"];
std::string_view id = objectData.json["id"];
if(startsWith(classId, CommandStations::classIdPrefix))
objectData.object = CommandStations::create(m_world, classId, id);
else if(startsWith(classId, Controllers::classIdPrefix))
objectData.object = Controllers::create(m_world, classId, id);
if(startsWith(classId, Interfaces::classIdPrefix))
objectData.object = Interfaces::create(m_world, classId, id);
else if(classId == Decoder::classId)
objectData.object = Decoder::create(m_world, id);
else if(classId == DecoderFunction::classId)
@ -171,10 +166,10 @@ void WorldLoader::createObject(ObjectData& objectData)
decoder->functions->items.appendInternal(f);
}
}
else if(startsWith(classId, Inputs::classIdPrefix))
objectData.object = Inputs::create(m_world, classId, id);
else if(startsWith(classId, Outputs::classIdPrefix))
objectData.object = Outputs::create(m_world, classId, id);
else if(classId == Input::classId)
objectData.object = Input::create(m_world, id);
else if(classId == Output::classId)
objectData.object = Output::create(m_world, id);
else if(classId == Board::classId)
objectData.object = Board::create(m_world, id);
else if(startsWith(classId, Tiles::classIdPrefix))

Datei anzeigen

@ -24,12 +24,7 @@
#include "../src/world/world.hpp"
#include "../src/board/board.hpp"
//#include "../src/hardware/commandstation/dccplusplusserial.hpp"
//#include "../src/hardware/commandstation/loconetserial.hpp"
//#include "../src/hardware/commandstation/loconettcpbinary.hpp"
//#include "../src/hardware/commandstation/rocoz21.hpp"
#include "../src/hardware/commandstation/virtualcommandstation.hpp"
//#include "../src/hardware/commandstation/xpressnetserial.hpp"
#include "../src/hardware/interface/loconetinterface.hpp"
TEST_CASE("Create world => destroy world", "[object-create-destroy]")
{
@ -77,49 +72,39 @@ TEST_CASE("Create world and board => destroy board", "[object-create-destroy]")
REQUIRE(worldWeak.expired());
}
TEMPLATE_TEST_CASE("Create world and command station => destroy world", "[object-create-destroy]"
//, DCCPlusPlusSerial
//, LocoNetSerial
//, LocoNetTCPBinary
//, RocoZ21
, VirtualCommandStation
//, XpressNetSerial
TEMPLATE_TEST_CASE("Create world and interface => destroy world", "[object-create-destroy]"
, LocoNetInterface
)
{
auto world = World::create();
std::weak_ptr<World> worldWeak = world;
REQUIRE_FALSE(worldWeak.expired());
std::weak_ptr<CommandStation> commandStationWeak = world->commandStations->add(TestType::classId);
REQUIRE_FALSE(commandStationWeak.expired());
REQUIRE(commandStationWeak.lock()->getClassId() == TestType::classId);
std::weak_ptr<TestType> interfaceWeak = std::dynamic_pointer_cast<TestType>(world->interfaces->add(TestType::classId));
REQUIRE_FALSE(interfaceWeak.expired());
REQUIRE(interfaceWeak.lock()->getClassId() == TestType::classId);
world.reset();
REQUIRE(commandStationWeak.expired());
REQUIRE(interfaceWeak.expired());
REQUIRE(worldWeak.expired());
}
TEMPLATE_TEST_CASE("Create world and command station => destroy command station", "[object-create-destroy]"
//, DCCPlusPlusSerial
//, LocoNetSerial
//, LocoNetTCPBinary
//, RocoZ21
, VirtualCommandStation
//, XpressNetSerial
TEMPLATE_TEST_CASE("Create world and interface => destroy interface", "[object-create-destroy]"
, LocoNetInterface
)
{
auto world = World::create();
std::weak_ptr<World> worldWeak = world;
REQUIRE_FALSE(worldWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 0);
REQUIRE(worldWeak.lock()->interfaces->length == 0);
std::weak_ptr<CommandStation> commandStationWeak = world->commandStations->add(TestType::classId);
REQUIRE_FALSE(commandStationWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
std::weak_ptr<TestType> interfaceWeak = std::dynamic_pointer_cast<TestType>(world->interfaces->add(TestType::classId));
REQUIRE_FALSE(interfaceWeak.expired());
REQUIRE(worldWeak.lock()->interfaces->length == 1);
world->commandStations->remove(commandStationWeak.lock());
REQUIRE(commandStationWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 0);
world->interfaces->remove(interfaceWeak.lock());
REQUIRE(interfaceWeak.expired());
REQUIRE(worldWeak.lock()->interfaces->length == 0);
world.reset();
REQUIRE(worldWeak.expired());
@ -216,39 +201,34 @@ TEST_CASE("Create world, decoder and function => destroy function", "[object-cre
REQUIRE(worldWeak.expired());
}
TEMPLATE_TEST_CASE("Create world, command station and decoder => destroy command station", "[object-create-destroy]"
//, DCCPlusPlusSerial
//, LocoNetSerial
//, LocoNetTCPBinary
//, RocoZ21
, VirtualCommandStation
//, XpressNetSerial
TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy interface", "[object-create-destroy]"
, LocoNetInterface
)
{
auto world = World::create();
std::weak_ptr<World> worldWeak = world;
REQUIRE_FALSE(worldWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 0);
REQUIRE(worldWeak.lock()->interfaces->length == 0);
REQUIRE(worldWeak.lock()->decoders->length == 0);
std::weak_ptr<CommandStation> commandStationWeak = world->commandStations->add(TestType::classId);
REQUIRE_FALSE(commandStationWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
std::weak_ptr<TestType> interfaceWeak = std::dynamic_pointer_cast<TestType>(world->interfaces->add(TestType::classId));
REQUIRE_FALSE(interfaceWeak.expired());
REQUIRE(worldWeak.lock()->interfaces->length == 1);
REQUIRE(worldWeak.lock()->decoders->length == 0);
REQUIRE(commandStationWeak.lock()->decoders->length == 0);
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
std::weak_ptr<Decoder> decoderWeak = commandStationWeak.lock()->decoders->add();
std::weak_ptr<Decoder> decoderWeak = interfaceWeak.lock()->decoders->add();
REQUIRE_FALSE(decoderWeak.expired());
REQUIRE(decoderWeak.lock()->commandStation.value() == commandStationWeak.lock());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak.lock()));
REQUIRE(worldWeak.lock()->interfaces->length == 1);
REQUIRE(worldWeak.lock()->decoders->length == 1);
REQUIRE(commandStationWeak.lock()->decoders->length == 1);
REQUIRE(interfaceWeak.lock()->decoders->length == 1);
world->commandStations->remove(commandStationWeak.lock());
REQUIRE(commandStationWeak.expired());
world->interfaces->remove(interfaceWeak.lock());
REQUIRE(interfaceWeak.expired());
REQUIRE_FALSE(decoderWeak.expired());
REQUIRE_FALSE(decoderWeak.lock()->commandStation.value().operator bool());
REQUIRE(worldWeak.lock()->commandStations->length == 0);
REQUIRE_FALSE(decoderWeak.lock()->interface.value().operator bool());
REQUIRE(worldWeak.lock()->interfaces->length == 0);
REQUIRE(worldWeak.lock()->decoders->length == 1);
world.reset();
@ -256,42 +236,37 @@ TEMPLATE_TEST_CASE("Create world, command station and decoder => destroy command
REQUIRE(worldWeak.expired());
}
TEMPLATE_TEST_CASE("Create world, command station and decoder => destroy decoder", "[object-create-destroy]"
//, DCCPlusPlusSerial
//, LocoNetSerial
//, LocoNetTCPBinary
//, RocoZ21
, VirtualCommandStation
//, XpressNetSerial
TEMPLATE_TEST_CASE("Create world, interface and decoder => destroy decoder", "[object-create-destroy]"
, LocoNetInterface
)
{
auto world = World::create();
std::weak_ptr<World> worldWeak = world;
REQUIRE_FALSE(worldWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 0);
REQUIRE(worldWeak.lock()->interfaces->length == 0);
REQUIRE(worldWeak.lock()->decoders->length == 0);
std::weak_ptr<CommandStation> commandStationWeak = world->commandStations->add(TestType::classId);
REQUIRE_FALSE(commandStationWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
std::weak_ptr<TestType> interfaceWeak = std::dynamic_pointer_cast<TestType>(world->interfaces->add(TestType::classId));
REQUIRE_FALSE(interfaceWeak.expired());
REQUIRE(worldWeak.lock()->interfaces->length == 1);
REQUIRE(worldWeak.lock()->decoders->length == 0);
REQUIRE(commandStationWeak.lock()->decoders->length == 0);
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
std::weak_ptr<Decoder> decoderWeak = commandStationWeak.lock()->decoders->add();
std::weak_ptr<Decoder> decoderWeak = interfaceWeak.lock()->decoders->add();
REQUIRE_FALSE(decoderWeak.expired());
REQUIRE(decoderWeak.lock()->commandStation.value() == commandStationWeak.lock());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
REQUIRE(decoderWeak.lock()->interface.value() == std::dynamic_pointer_cast<DecoderController>(interfaceWeak.lock()));
REQUIRE(worldWeak.lock()->interfaces->length == 1);
REQUIRE(worldWeak.lock()->decoders->length == 1);
REQUIRE(commandStationWeak.lock()->decoders->length == 1);
REQUIRE(interfaceWeak.lock()->decoders->length == 1);
world->decoders->remove(decoderWeak.lock());
REQUIRE_FALSE(commandStationWeak.expired());
REQUIRE_FALSE(interfaceWeak.expired());
REQUIRE(decoderWeak.expired());
REQUIRE(worldWeak.lock()->commandStations->length == 1);
REQUIRE(worldWeak.lock()->interfaces->length == 1);
REQUIRE(worldWeak.lock()->decoders->length == 0);
REQUIRE(commandStationWeak.lock()->decoders->length == 0);
REQUIRE(interfaceWeak.lock()->decoders->length == 0);
world.reset();
REQUIRE(commandStationWeak.expired());
REQUIRE(interfaceWeak.expired());
REQUIRE(worldWeak.expired());
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/input/inputs.hpp
* shared/src/enum/interfacestatus.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -20,25 +20,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTS_HPP
#ifndef TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_INTERFACESTATUS_HPP
#define TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_INTERFACESTATUS_HPP
#include "input.hpp"
#include "../../utils/makearray.hpp"
#include <cstdint>
#include "enum.hpp"
#include "loconetinput.hpp"
#include "xpressnetinput.hpp"
struct Inputs
enum class InterfaceStatus : uint8_t
{
static constexpr std::string_view classIdPrefix = "input.";
static constexpr auto classList = makeArray(
LocoNetInput::classId,
XpressNetInput::classId
);
static std::shared_ptr<Input> create(const std::shared_ptr<World>& world, std::string_view classId, std::string_view id);
Offline = 0,
Initializing = 1,
Online = 2,
Error = 255,
};
#endif
ENUM_NAME(InterfaceStatus, "interface_status")
ENUM_VALUES(InterfaceStatus, 4,
{
{InterfaceStatus::Offline, "offline"},
{InterfaceStatus::Initializing, "initializing"},
{InterfaceStatus::Online, "online"},
{InterfaceStatus::Error, "error"},
})
#endif

Datei anzeigen

@ -121,9 +121,13 @@ direction:reverse=Reverse
hardware:address=Address
hardware:command_station=Command station
hardware:decoders=Decoders
hardware:input_monitor=Input monitor
hardware:inputs=Inputs
hardware:interface=Interface
hardware:loconet=LocoNet
hardware:output_keyboard=Output keyboard
hardware:outputs=Outputs
hardware:speed_steps=Speed steps
hardware:xpressnet=XpressNet
@ -133,6 +137,8 @@ input_map_item.block:input=Input
input_map_item.block:invert=Invert
input_map_item.block:type=Type
interface:status=Status
ip:hostname=Hostname
ip:port=Port
@ -433,6 +439,7 @@ world:controllers=Controllers
world:decoders=Decoders
world:edit=Edit world
world:inputs=Inputs
world:interfaces=Interfaces
world:lua_scripts=Lua scripts
world:mute=Mute
world:no_smoke=No smoke