Replaced command station and controller by interface with supporting controllers
Dieser Commit ist enthalten in:
Ursprung
48d53f60f2
Commit
956c8ad168
@ -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")); });
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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
|
||||
51
server/src/core/controllerlist.hpp
Normale Datei
51
server/src/core/controllerlist.hpp
Normale Datei
@ -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
|
||||
79
server/src/core/controllerlistbase.cpp
Normale Datei
79
server/src/core/controllerlistbase.cpp
Normale Datei
@ -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));
|
||||
}
|
||||
|
||||
59
server/src/core/controllerlistbase.hpp
Normale Datei
59
server/src/core/controllerlistbase.hpp
Normale Datei
@ -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
|
||||
81
server/src/core/controllerlistbasetablemodel.cpp
Normale Datei
81
server/src/core/controllerlistbasetablemodel.cpp
Normale Datei
@ -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);
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
|
||||
@ -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()))
|
||||
{
|
||||
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
83
server/src/hardware/decoder/decodercontroller.cpp
Normale Datei
83
server/src/hardware/decoder/decodercontroller.cpp
Normale Datei
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
54
server/src/hardware/decoder/decodercontroller.hpp
Normale Datei
54
server/src/hardware/decoder/decodercontroller.hpp
Normale Datei
@ -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
|
||||
@ -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",
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
103
server/src/hardware/input/inputcontroller.cpp
Normale Datei
103
server/src/hardware/input/inputcontroller.cpp
Normale Datei
@ -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;
|
||||
}
|
||||
101
server/src/hardware/input/inputcontroller.hpp
Normale Datei
101
server/src/hardware/input/inputcontroller.hpp
Normale Datei
@ -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
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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
|
||||
88
server/src/hardware/interface/interface.cpp
Normale Datei
88
server/src/hardware/interface/interface.cpp
Normale Datei
@ -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;
|
||||
}
|
||||
}
|
||||
50
server/src/hardware/interface/interface.hpp
Normale Datei
50
server/src/hardware/interface/interface.hpp
Normale Datei
@ -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
|
||||
80
server/src/hardware/interface/interfacelist.cpp
Normale Datei
80
server/src/hardware/interface/interfacelist.cpp
Normale Datei
@ -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);
|
||||
}
|
||||
@ -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
|
||||
83
server/src/hardware/interface/interfacelisttablemodel.cpp
Normale Datei
83
server/src/hardware/interface/interfacelisttablemodel.cpp
Normale Datei
@ -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);
|
||||
}
|
||||
@ -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
|
||||
@ -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>();
|
||||
}
|
||||
@ -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
|
||||
@ -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));
|
||||
}
|
||||
55
server/src/hardware/output/keyboard/outputkeyboard.cpp
Normale Datei
55
server/src/hardware/output/keyboard/outputkeyboard.cpp
Normale Datei
@ -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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
103
server/src/hardware/output/outputcontroller.cpp
Normale Datei
103
server/src/hardware/output/outputcontroller.cpp
Normale Datei
@ -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;
|
||||
}
|
||||
106
server/src/hardware/output/outputcontroller.hpp
Normale Datei
106
server/src/hardware/output/outputcontroller.hpp
Normale Datei
@ -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
|
||||
44
server/src/hardware/protocol/dcc/dcc.hpp
Normale Datei
44
server/src/hardware/protocol/dcc/dcc.hpp
Normale Datei
@ -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
|
||||
@ -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");
|
||||
|
||||
@ -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
40
server/src/utils/inrange.hpp
Normale Datei
@ -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
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren