Merge branch 'interface-controller'

Dieser Commit ist enthalten in:
Reinder Feenstra 2022-01-03 01:00:34 +01:00
Commit 1966cf666b
237 geänderte Dateien mit 14777 neuen und 6711 gelöschten Zeilen

Datei anzeigen

@ -28,7 +28,7 @@ jobs:
jobs: 2
build_type: Release
build_deb: true
defines: "-DDEBIAN_PACKAGE_VERSION_EXTRA=~ubuntu~bionic~$CI_REF_NAME_SLUG~${{github.run_number}}~$CI_SHA_SHORT"
defines: "-DCMAKE_CXX_COMPILER=g++-8 -DDEBIAN_PACKAGE_VERSION_EXTRA=~ubuntu~bionic~$CI_REF_NAME_SLUG~${{github.run_number}}~$CI_SHA_SHORT"
- name: "ubuntu_20.04"
os: ubuntu-20.04
@ -98,6 +98,11 @@ jobs:
- name: Install packages
if: startswith(matrix.config.os, 'ubuntu')
run: sudo apt install qt5-default libqt5svg5-dev
# Ubuntu 18.04 only:
- name: Install g++-8
if: matrix.config.os == 'ubuntu-18.04'
run: sudo apt install g++-8
# All:
- name: Create Build Environment
@ -173,7 +178,7 @@ jobs:
jobs: 2
build_type: Release
build_deb: true
defines: "-DDEBIAN_PACKAGE_VERSION_EXTRA=~ubuntu~bionic~$CI_REF_NAME_SLUG~${{github.run_number}}~$CI_SHA_SHORT"
defines: "-DCMAKE_CXX_COMPILER=g++-8 -DDEBIAN_PACKAGE_VERSION_EXTRA=~ubuntu~bionic~$CI_REF_NAME_SLUG~${{github.run_number}}~$CI_SHA_SHORT"
ccov: false
- name: "ubuntu_20.04"
@ -242,6 +247,11 @@ jobs:
if: startswith(matrix.config.os, 'ubuntu')
run: sudo apt install liblua5.3-dev lcov libarchive-dev
# Ubuntu 18.04 only:
- name: Install g++-8
if: matrix.config.os == 'ubuntu-18.04'
run: sudo apt install g++-8
# All:
- name: Create Build Environment
# Some projects don't allow in-source building, so create a separate build directory

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -28,6 +28,7 @@
#include <traintastic/enum/decoderprotocol.hpp>
#include <traintastic/enum/direction.hpp>
#include <traintastic/enum/lengthunit.hpp>
#include <traintastic/enum/loconetinterfacetype.hpp>
#include <traintastic/enum/loconetcommandstation.hpp>
#include <traintastic/enum/loconetserialinterface.hpp>
#include <traintastic/enum/outputaction.hpp>
@ -39,7 +40,8 @@
#include <traintastic/enum/weightunit.hpp>
#include <traintastic/enum/worldscale.hpp>
#include <traintastic/enum/xpressnetcommandstation.hpp>
#include <traintastic/enum/xpressnetserialinterface.hpp>
#include <traintastic/enum/xpressnetinterfacetype.hpp>
#include <traintastic/enum/xpressnetserialinterfacetype.hpp>
#include "../settings/boardsettings.hpp"
#define GET_ENUM_VALUES(_type) \
@ -73,6 +75,7 @@ QString translateEnum(const QString& enumName, qint64 value)
TRANSLATE_ENUM(Direction)
TRANSLATE_ENUM(LengthUnit)
TRANSLATE_ENUM(LocoNetCommandStation)
TRANSLATE_ENUM(LocoNetInterfaceType)
TRANSLATE_ENUM(LocoNetSerialInterface)
TRANSLATE_ENUM(OutputAction)
TRANSLATE_ENUM(SensorType)
@ -83,7 +86,8 @@ QString translateEnum(const QString& enumName, qint64 value)
TRANSLATE_ENUM(WeightUnit)
TRANSLATE_ENUM(WorldScale)
TRANSLATE_ENUM(XpressNetCommandStation)
TRANSLATE_ENUM(XpressNetSerialInterface)
TRANSLATE_ENUM(XpressNetInterfaceType)
TRANSLATE_ENUM(XpressNetSerialInterfaceType)
TRANSLATE_ENUM(BoardSettings::ColorScheme)
return enumName + "@" + QString::number(value);
}

Datei anzeigen

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

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -49,12 +49,16 @@
ObjectListWidget::ObjectListWidget(const ObjectPtr& object, QWidget* parent) :
QWidget(parent),
m_requestIdInputMonitor{Connection::invalidRequestId},
m_requestIdOutputKeyboard{Connection::invalidRequestId},
m_object{object},
m_toolbar{new QToolBar()},
m_buttonAdd{nullptr},
m_actionAdd{nullptr},
m_actionEdit{nullptr},
m_actionDelete{nullptr},
m_actionInputMonitor{nullptr},
m_actionOutputKeyboard{nullptr},
m_tableWidget{new TableWidget()}
{
m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -188,6 +192,43 @@ ObjectListWidget::ObjectListWidget(const ObjectPtr& object, QWidget* parent) :
m_actionDelete->setForceDisabled(true);
m_toolbar->addAction(m_actionDelete);
}
if(Method* method = m_object->getMethod("input_monitor"))
{
m_actionInputMonitor = new MethodAction(Theme::getIcon("input_monitor"), *method,
[this]()
{
m_requestIdInputMonitor = m_actionInputMonitor->method().call(
[this](const ObjectPtr& inputMonitor, Message::ErrorCode)
{
if(inputMonitor)
MainWindow::instance->showObject(inputMonitor);
});
});
}
if(Method* method = m_object->getMethod("output_keyboard"))
{
m_actionOutputKeyboard = new MethodAction(Theme::getIcon("output_keyboard"), *method,
[this]()
{
m_requestIdOutputKeyboard = m_actionOutputKeyboard->method().call(
[this](const ObjectPtr& outputKeyboard, Message::ErrorCode)
{
if(outputKeyboard)
MainWindow::instance->showObject(outputKeyboard);
});
});
}
if(m_actionInputMonitor || m_actionOutputKeyboard)
{
m_toolbar->addSeparator();
if(m_actionInputMonitor)
m_toolbar->addAction(m_actionInputMonitor);
if(m_actionOutputKeyboard)
m_toolbar->addAction(m_actionOutputKeyboard);
}
}
ObjectListWidget::~ObjectListWidget()

Datei anzeigen

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

Datei anzeigen

@ -91,7 +91,7 @@ ThrottleWidget::ThrottleWidget(ObjectPtr object, QWidget* parent)
});
}
if(m_emergencyStop = m_object->getProperty("emergency_stop"))
if((m_emergencyStop = m_object->getProperty("emergency_stop")))
{
m_speedoMeter->setEStop(m_emergencyStop->toBool());
connect(m_emergencyStop, &AbstractProperty::valueChangedBool, m_speedoMeter, &SpeedoMeterWidget::setEStop);
@ -100,7 +100,7 @@ ThrottleWidget::ThrottleWidget(ObjectPtr object, QWidget* parent)
if(auto* p = dynamic_cast<ObjectProperty*>(m_object->getProperty("functions")))
{
m_functionsRequestId = m_object->connection()->getObject(p->objectId(),
[this](const ObjectPtr& functions, Message::ErrorCode ec)
[this](const ObjectPtr& functions, Message::ErrorCode /*ec*/)
{
m_functionsRequestId = Connection::invalidRequestId;
@ -113,7 +113,7 @@ ThrottleWidget::ThrottleWidget(ObjectPtr object, QWidget* parent)
for(const QString& id : *items)
{
const int functionRequestId = functions->connection()->getObject(id,
[this, i](const ObjectPtr& function, Message::ErrorCode ec)
[this, i](const ObjectPtr& function, Message::ErrorCode /*ec*/)
{
m_functionRequestIds.erase(i);
@ -263,9 +263,7 @@ ThrottleFunctionButton* ThrottleWidget::getFunctionButton(DecoderFunctionFunctio
auto it = std::find_if(m_functionButtons.begin(), m_functionButtons.end(),
[function](const auto* btn)
{
auto n = btn->number();
auto f = btn->function();
return f == function;
return btn->function() == function;
});
return (it != m_functionButtons.end()) ? *it : nullptr;
}

Datei anzeigen

@ -77,10 +77,6 @@ file(GLOB SOURCES
"src/core/*.hpp"
"src/core/*.cpp"
"src/enum/*.hpp"
"src/hardware/commandstation/*.hpp"
"src/hardware/commandstation/*.cpp"
"src/hardware/controller/*.hpp"
"src/hardware/controller/*.cpp"
"src/hardware/decoder/*.hpp"
"src/hardware/decoder/*.cpp"
"src/hardware/input/*.hpp"
@ -91,6 +87,8 @@ file(GLOB SOURCES
"src/hardware/input/map/*.cpp"
"src/hardware/input/monitor/*.hpp"
"src/hardware/input/monitor/*.cpp"
"src/hardware/interface/*.hpp"
"src/hardware/interface/*.cpp"
"src/hardware/output/*.hpp"
"src/hardware/output/*.cpp"
"src/hardware/output/keyboard/*.hpp"
@ -101,12 +99,26 @@ file(GLOB SOURCES
"src/hardware/output/map/*.cpp"
"src/hardware/protocol/dccplusplus/*.hpp"
"src/hardware/protocol/dccplusplus/*.cpp"
"src/hardware/protocol/dccplusplus/iohandler/*.hpp"
"src/hardware/protocol/dccplusplus/iohandler/*.cpp"
"src/hardware/protocol/ecos/*.hpp"
"src/hardware/protocol/ecos/*.cpp"
"src/hardware/protocol/ecos/iohandler/*.hpp"
"src/hardware/protocol/ecos/iohandler/*.cpp"
"src/hardware/protocol/ecos/object/*.hpp"
"src/hardware/protocol/ecos/object/*.cpp"
"src/hardware/protocol/loconet/*.hpp"
"src/hardware/protocol/loconet/*.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/xpressnet/iohandler/*.hpp"
"src/hardware/protocol/xpressnet/iohandler/*.cpp"
"src/hardware/protocol/z21/*.hpp"
"src/hardware/protocol/z21/*.cpp"
"src/hardware/protocol/z21/iohandler/*.hpp"
"src/hardware/protocol/z21/iohandler/*.cpp"
"src/log/*.hpp"
"src/log/*.cpp"
"src/train/*.hpp"
@ -212,29 +224,6 @@ else()
add_definitions(-DDISABLE_LUA_SCRIPTING)
endif()
option(USB_XPRESSNET "USB XpressNet interface/controller support" ON)
message(STATUS "USB XpressNet interface/controller support: ${USB_XPRESSNET}")
if(ENABLE_USB_XPRESSNET_INTERFACE)
#pkg_check_modules(USBXPRESSNET REQUIRED IMPORTED_TARGET usbxpressnet)
find_path(USBXPRESSNET_INCLUDE_DIR usbxpressnet.h)
#message(${USBXPRESSNET_INCLUDE_DIR})
find_library(USBXPRESSNET_LIB usbxpressnet)
#message(${USBXPRESSNET_LIB})
#if(NOT USBXPRESSNET_LIB)
# message(FATAL_ERROR "usbxpressnet library not found")
#endif()
add_definitions(-DUSB_XPRESSNET)
target_include_directories(traintastic-server PRIVATE ${USBXPRESSNET_INCLUDE_DIR})
target_link_libraries(traintastic-server PRIVATE ${USBXPRESSNET_LIB})
target_include_directories(traintastic-server-test PRIVATE ${USBXPRESSNET_INCLUDE_DIR})
target_link_libraries(traintastic-server-test PRIVATE ${USBXPRESSNET_LIB})
else()
list(FILTER SOURCES EXCLUDE REGEX ".*usbxpressnet(interface|controller)\.(hpp|cpp)$")
endif()
### OPTIONS END ###
target_sources(traintastic-server PRIVATE ${SOURCES})

52
server/simulate/ecossimulator.py Ausführbare Datei
Datei anzeigen

@ -0,0 +1,52 @@
#!/usr/bin/env python3
import socket
import re
ADDRESS = '127.0.0.1'
PORT = 15471
OBJECTID_ECOS = 1
def process(request):
print('RX:\n' + request)
m = re.match(r'^(queryObjects|set|get|create|delete|request|release)\(\s*([0-9]+)\s*(.*)\)$', request)
if m is not None:
command = m.group(1)
objectId = int(m.group(2))
args = [s for s in re.split(r'\s*,\s*', m.group(3)) if s]
if objectId == OBJECTID_ECOS:
if command == 'get':
if args[0] == 'info':
return \
'<REPLY get(1, info)>\n' + \
'1 ECoS\n' + \
'1 ProtocolVersion[0.1]\n' + \
'1 ApplicationVersion[1.0.1]\n' + \
'1 HardwareVersion[1.3]\n' + \
'<END 0 (OK)>\n'
return None
print('Traintastic ECoS simulator')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((ADDRESS, PORT))
s.listen()
print('Listening at {:s}:{:d}'.format(ADDRESS, PORT))
conn, addr = s.accept()
with conn:
print('Connection from {:s}:{:d}'.format(addr[0], addr[1]))
while True:
try:
for request in conn.recv(1024).decode('utf-8').split('\n'):
if request != '':
response = process(request)
if response is not None:
print('TX:\n' + response)
conn.sendall(response.encode('utf-8'))
except ConnectionError:
print('Connection lost')
break

Datei anzeigen

@ -62,6 +62,12 @@ struct Attributes
item.setAttribute(AttributeName::Enabled, value);
}
static inline void setEnabled(std::initializer_list<std::reference_wrapper<InterfaceItem>> items, bool value)
{
for(auto& item : items)
item.get().setAttribute(AttributeName::Enabled, value);
}
template<typename T>
static inline void addMinMax(Property<T>& property, T min, T max)
{
@ -106,6 +112,22 @@ struct Attributes
property.setAttribute(AttributeName::Max, convertUnit(value, unit, property.unit()));
}
template<typename T>
static inline void setMinMax(Property<T>& property, T min, T max)
{
static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>);
property.setAttribute(AttributeName::Min, min);
property.setAttribute(AttributeName::Max, max);
}
template<class T, class Unit>
static inline void setMinMax(UnitProperty<T, Unit>& property, T min, T max, Unit unit)
{
static_assert(std::is_floating_point_v<T>);
property.setAttribute(AttributeName::Min, convertUnit(min, unit, property.unit()));
property.setAttribute(AttributeName::Max, convertUnit(max, unit, property.unit()));
}
static inline void addVisible(InterfaceItem& item, bool value)
{
item.addAttribute(AttributeName::Visible, value);

Datei anzeigen

@ -1,140 +0,0 @@
/**
* server/src/core/commandstationproperty.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "commandstationproperty.hpp"
#include "../hardware/commandstation/commandstation.hpp"
using T = CommandStation;
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags) :
AbstractObjectProperty(object, name, flags),
m_value{value}
{
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags) :
CommandStationProperty(object, name, std::shared_ptr<T>(), flags)
{
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<T>& value, PropertyFlags flags, OnSet onSet) :
CommandStationProperty(object, name, value, flags)
{
m_onSet = onSet;
}
CommandStationProperty::CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags, OnSet onSet) :
CommandStationProperty(object, name, std::shared_ptr<T>(), flags, onSet)
{
}
const std::shared_ptr<T>& CommandStationProperty::value() const
{
return m_value;
}
void CommandStationProperty::setValue(const std::shared_ptr<T>& value)
{
assert(isWriteable());
if(m_value == value)
return;
else if(!isWriteable())
throw not_writable_error();
else if(!m_onSet || m_onSet(value))
{
m_value = value;
changed();
}
else
throw invalid_value_error();
}
void CommandStationProperty::setValueInternal(const std::shared_ptr<T>& value)
{
if(m_value != value)
{
m_value = value;
changed();
}
}
const T* CommandStationProperty::operator ->() const
{
return m_value.get();
}
T* CommandStationProperty::operator ->()
{
return m_value.get();
}
const T& CommandStationProperty::operator *() const
{
return *m_value;
}
T& CommandStationProperty::operator *()
{
return *m_value;
}
CommandStationProperty::operator bool()
{
return m_value.operator bool();
}
CommandStationProperty& CommandStationProperty::operator =(const std::shared_ptr<T>& value)
{
setValue(value);
return *this;
}
ObjectPtr CommandStationProperty::toObject() const
{
return std::dynamic_pointer_cast<Object>(m_value);
}
void CommandStationProperty::fromObject(const ObjectPtr& value)
{
if(value)
{
if(std::shared_ptr<T> v = std::dynamic_pointer_cast<T>(value))
setValue(v);
else
throw conversion_error();
}
else
setValue(nullptr);
}
void CommandStationProperty::load(const ObjectPtr& value)
{
if(value)
{
if(std::shared_ptr<T> v = std::dynamic_pointer_cast<T>(value))
m_value = v;
else
throw conversion_error();
}
else
m_value.reset();
}

Datei anzeigen

@ -1,68 +0,0 @@
/**
* server/src/core/commandstationproperty.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_CORE_COMMANDSTATIONPROPERTY_HPP
#define TRAINTASTIC_SERVER_CORE_COMMANDSTATIONPROPERTY_HPP
#include "abstractobjectproperty.hpp"
#include <functional>
class CommandStation;
//! workaround for ObjectProperty<CommandStation>
class CommandStationProperty : public AbstractObjectProperty
{
public:
using OnSet = std::function<bool(const std::shared_ptr<CommandStation>& value)>;
protected:
std::shared_ptr<CommandStation> m_value;
OnSet m_onSet;
public:
CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<CommandStation>& value, PropertyFlags flags);
CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags);
CommandStationProperty(Object* object, const std::string& name, const std::shared_ptr<CommandStation>& value, PropertyFlags flags, OnSet onSet);
CommandStationProperty(Object* object, const std::string& name, std::nullptr_t, PropertyFlags flags, OnSet onSet);
const std::shared_ptr<CommandStation>& value() const;
void setValue(const std::shared_ptr<CommandStation>& value);
void setValueInternal(const std::shared_ptr<CommandStation>& value);
const CommandStation* operator ->() const;
CommandStation* operator ->();
const CommandStation& operator *() const;
CommandStation& operator *();
operator bool();
CommandStationProperty& operator =(const std::shared_ptr<CommandStation>& value);
ObjectPtr toObject() const final;
void fromObject(const ObjectPtr& value) final;
void load(const ObjectPtr& value) final;
};
#endif

Datei anzeigen

@ -0,0 +1,51 @@
/**
* server/src/core/controllerlist.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_CORE_CONTROLLERLIST_HPP
#define TRAINTASTIC_SERVER_CORE_CONTROLLERLIST_HPP
#include "controllerlistbase.hpp"
template<class T>
class ControllerList : public ControllerListBase
{
public:
ControllerList(Object& _parent, const std::string& parentPropertyName)
: ControllerListBase(_parent, parentPropertyName)
{
}
inline void add(const std::shared_ptr<T>& controller)
{
if(ObjectPtr object = std::dynamic_pointer_cast<Object>(controller))
ControllerListBase::add(std::move(object));
else
assert(false);
}
inline void remove(const std::shared_ptr<T>& controller)
{
ControllerListBase::remove(std::dynamic_pointer_cast<Object>(controller));
}
};
#endif

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,61 @@
/**
* 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;
ObjectPtr getObject(uint32_t index) final;
};
#endif

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/hardware/input/xpressnetlisttablemodel.cpp
* server/src/core/controllerlistbasetablemodel.cpp
*
* This file is part of the traintastic source code.
*
@ -20,35 +20,48 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "xpressnetlisttablemodel.hpp"
#include "xpressnetlist.hpp"
#include "../../../utils/displayname.hpp"
#include "controllerlistbasetablemodel.hpp"
#include "controllerlistbase.hpp"
#include "../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
bool XpressNetListTableModel::isListedProperty(const std::string& name)
bool ControllerListBaseTableModel::isListedProperty(const std::string& name)
{
return name == "id";
return
name == "id" ||
name == "name";
}
XpressNetListTableModel::XpressNetListTableModel(XpressNetList& list) :
ObjectListTableModel<XpressNet::XpressNet>(list)
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::id,
DisplayName::Object::name,
});
}
std::string XpressNetListTableModel::getText(uint32_t column, uint32_t row) const
std::string ControllerListBaseTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const XpressNet::XpressNet& xpressnet = getItem(row);
const Object& object = *m_list->m_items[row];
switch(column)
{
case columnId:
return xpressnet.getObjectId();
return object.getObjectId();
case columnName:
if(const auto* property = object.getProperty("name"))
return property->toString();
break;
default:
assert(false);
@ -59,8 +72,10 @@ std::string XpressNetListTableModel::getText(uint32_t column, uint32_t row) cons
return "";
}
void XpressNetListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
void ControllerListBaseTableModel::propertyChanged(BaseProperty& property, uint32_t row)
{
if(property.name() == "id")
changed(row, columnId);
else if(property.name() == "name")
changed(row, columnName);
}

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/hardware/protocol/xpressnet/xpressnetlisttablemodel.hpp
* server/src/core/controllerlistbasetablemodel.hpp
*
* This file is part of the traintastic source code.
*
@ -20,27 +20,28 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_XPRESSNET_XPRESSNETLISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_XPRESSNET_XPRESSNETLISTTABLEMODEL_HPP
#ifndef TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASETABLEMODEL_HPP
#define TRAINTASTIC_SERVER_CORE_CONTROLLERLISTBASETABLEMODEL_HPP
#include "../../../core/objectlisttablemodel.hpp"
#include "xpressnet.hpp"
#include "tablemodel.hpp"
class XpressNetList;
class ControllerListBase;
class XpressNetListTableModel : public ObjectListTableModel<XpressNet::XpressNet>
class ControllerListBaseTableModel final : public TableModel
{
friend class XpressNetList;
CLASS_ID("table_model.controller_list")
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;
friend class ControllerListBase;
private:
std::shared_ptr<ControllerListBase> m_list;
void propertyChanged(BaseProperty& property, uint32_t row);
public:
CLASS_ID("xpressnet_list_table_model")
static bool isListedProperty(const std::string& name);
XpressNetListTableModel(XpressNetList& list);
ControllerListBaseTableModel(ControllerListBase& list);
std::string getText(uint32_t column, uint32_t row) const final;
};

Datei anzeigen

@ -45,21 +45,41 @@ void Object::destroy()
}
}
const InterfaceItem* Object::getItem(std::string_view name) const
{
return m_interfaceItems.find(name);
}
InterfaceItem* Object::getItem(std::string_view name)
{
return m_interfaceItems.find(name);
}
const AbstractMethod* Object::getMethod(std::string_view name) const
{
return dynamic_cast<const AbstractMethod*>(getItem(name));
}
AbstractMethod* Object::getMethod(std::string_view name)
{
return dynamic_cast<AbstractMethod*>(getItem(name));
}
const AbstractProperty* Object::getProperty(std::string_view name) const
{
return dynamic_cast<const AbstractProperty*>(getItem(name));
}
AbstractProperty* Object::getProperty(std::string_view name)
{
return dynamic_cast<AbstractProperty*>(getItem(name));
}
const AbstractVectorProperty* Object::getVectorProperty(std::string_view name) const
{
return dynamic_cast<const AbstractVectorProperty*>(getItem(name));
}
AbstractVectorProperty* Object::getVectorProperty(std::string_view name)
{
return dynamic_cast<AbstractVectorProperty*>(getItem(name));

Datei anzeigen

@ -99,9 +99,13 @@ class Object : public std::enable_shared_from_this<Object>
const InterfaceItems& interfaceItems() const { return m_interfaceItems; }
const InterfaceItem* getItem(std::string_view name) const;
InterfaceItem* getItem(std::string_view name);
const AbstractMethod* getMethod(std::string_view name) const;
AbstractMethod* getMethod(std::string_view name);
const AbstractProperty* getProperty(std::string_view name) const;
AbstractProperty* getProperty(std::string_view name);
const AbstractVectorProperty* getVectorProperty(std::string_view name) const;
AbstractVectorProperty* getVectorProperty(std::string_view name);
};

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -0,0 +1,35 @@
/**
* server/src/enum/interfacestatus.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_ENUM_INTERFACESTATUS_HPP
#define TRAINTASTIC_SERVER_ENUM_INTERFACESTATUS_HPP
#include <traintastic/enum/interfacestatus.hpp>
inline constexpr std::array<InterfaceStatus, 4> interfaceStatusValues{{
InterfaceStatus::Offline,
InterfaceStatus::Initializing,
InterfaceStatus::Online,
InterfaceStatus::Error,
}};
#endif

Datei anzeigen

@ -0,0 +1,49 @@
/**
* server/src/enum/loconetinterface.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_ENUM_LOCONETINTERFACETYPE_HPP
#define TRAINTASTIC_SERVER_ENUM_LOCONETINTERFACETYPE_HPP
#include <traintastic/enum/loconetinterfacetype.hpp>
#include <array>
inline constexpr std::array<LocoNetInterfaceType, 4> locoNetInterfaceTypeValues{{
LocoNetInterfaceType::Serial,
LocoNetInterfaceType::TCPBinary,
LocoNetInterfaceType::LBServer,
LocoNetInterfaceType::Z21,
}};
constexpr bool isSerial(LocoNetInterfaceType value)
{
return (value == LocoNetInterfaceType::Serial);
}
constexpr bool isNetwork(LocoNetInterfaceType value)
{
return
value == LocoNetInterfaceType::TCPBinary ||
value == LocoNetInterfaceType::LBServer ||
value == LocoNetInterfaceType::Z21;
}
#endif

Datei anzeigen

@ -0,0 +1,34 @@
/**
* server/src/enum/xpressnetinterface.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_ENUM_XPRESSNETINTERFACETYPE_HPP
#define TRAINTASTIC_SERVER_ENUM_XPRESSNETINTERFACETYPE_HPP
#include <traintastic/enum/xpressnetinterfacetype.hpp>
#include <array>
inline constexpr std::array<XpressNetInterfaceType, 2> xpressNetInterfaceTypeValues{{
XpressNetInterfaceType::Serial,
XpressNetInterfaceType::Network,
}};
#endif

Datei anzeigen

@ -1,37 +0,0 @@
/**
* server/src/enum/xpressnetserialinterface.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_ENUM_XPRESSNETSERIALINTERFACE_HPP
#define TRAINTASTIC_SERVER_ENUM_XPRESSNETSERIALINTERFACE_HPP
#include <traintastic/enum/xpressnetserialinterface.hpp>
#include <array>
inline constexpr std::array<XpressNetSerialInterface, 5> XpressNetSerialInterfaceValues{{
XpressNetSerialInterface::Custom,
XpressNetSerialInterface::LenzLI100,
XpressNetSerialInterface::LenzLI100F,
XpressNetSerialInterface::LenzLI101F,
XpressNetSerialInterface::RoSoftS88XPressNetLI,
}};
#endif

Datei anzeigen

@ -1,5 +1,5 @@
/**
* server/src/hardware/input/inputs.hpp
* server/src/enum/xpressnetserialinterfacetype.hpp
*
* This file is part of the traintastic source code.
*
@ -20,25 +20,19 @@
* 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_SERVER_ENUM_XPRESSNETSERIALINTERFACETYPE_HPP
#define TRAINTASTIC_SERVER_ENUM_XPRESSNETSERIALINTERFACETYPE_HPP
#include "input.hpp"
#include "../../utils/makearray.hpp"
#include <traintastic/enum/xpressnetserialinterfacetype.hpp>
#include <array>
#include "loconetinput.hpp"
#include "xpressnetinput.hpp"
constexpr std::array<XpressNetSerialInterfaceType, 6> XpressNetSerialInterfaceTypeValues{{
XpressNetSerialInterfaceType::DigikeijsDR5000,
XpressNetSerialInterfaceType::LenzLI100,
XpressNetSerialInterfaceType::LenzLI100F,
XpressNetSerialInterfaceType::LenzLI101F,
XpressNetSerialInterfaceType::LenzLIUSB,
XpressNetSerialInterfaceType::RoSoftS88XPressNetLI,
}};
struct Inputs
{
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);
};
#endif
#endif

Datei anzeigen

@ -1,204 +0,0 @@
/**
* server/src/hardware/commandstation/commandstation.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 "commandstation.hpp"
#include "commandstationlist.hpp"
#include "commandstationlisttablemodel.hpp"
#include <functional>
#include "../../world/world.hpp"
#include "../decoder/decoder.hpp"
#include "../decoder/decoderlist.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/almostzero.hpp"
CommandStation::CommandStation(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,
[this](bool value)
{
Attributes::setEnabled(emergencyStop, value);
Attributes::setEnabled(powerOn, value);
},
std::bind(&CommandStation::setOnline, this, std::placeholders::_1)},
//status{this, "status", CommandStationStatus::Offline, PropertyFlags::ReadOnly},
emergencyStop{this, "emergency_stop", true, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
[this](bool value)
{
emergencyStopChanged(value);
}},
powerOn{this, "power_on", false, PropertyFlags::ReadWrite | PropertyFlags::NoStore,
[this](bool value)
{
powerOnChanged(value);
}},
decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
controllers{this, "controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
{
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
controllers.setValueInternal(std::make_shared<ControllerList>(*this, controllers.name()));
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(emergencyStop, DisplayName::CommandStation::emergencyStop);
Attributes::addEnabled(emergencyStop, online);
Attributes::addObjectEditor(emergencyStop, false);
m_interfaceItems.insertBefore(emergencyStop, notes);
Attributes::addDisplayName(powerOn, DisplayName::CommandStation::powerOn);
Attributes::addEnabled(powerOn, online);
Attributes::addObjectEditor(powerOn, false);
m_interfaceItems.insertBefore(powerOn, notes);
Attributes::addDisplayName(decoders, DisplayName::World::decoders);
m_interfaceItems.add(decoders);
Attributes::addDisplayName(controllers, DisplayName::World::controllers);
m_interfaceItems.add(controllers);
Attributes::addDisplayName(notes, DisplayName::Object::notes);
m_interfaceItems.add(notes);
}
void CommandStation::loaded()
{
IdObject::loaded();
checkAllDecoders();
}
void CommandStation::addToWorld()
{
IdObject::addToWorld();
if(auto world = m_world.lock())
world->commandStations->addObject(shared_ptr<CommandStation>());
}
void CommandStation::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->commandStation.value() == shared_ptr<CommandStation>());
decoder->commandStation = nullptr;
}
if(auto world = m_world.lock())
world->commandStations->removeObject(shared_ptr<CommandStation>());
IdObject::destroying();
}
void CommandStation::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
Attributes::setEnabled(name, contains(state, WorldState::Edit));
try
{
switch(event)
{
case WorldEvent::Offline:
online = false;
break;
case WorldEvent::Online:
online = true;
break;
case WorldEvent::PowerOff:
powerOn = false;
break;
case WorldEvent::PowerOn:
powerOn = true;
if(!emergencyStop)
restoreSpeed();
break;
case WorldEvent::Stop:
emergencyStop = true;
break;
case WorldEvent::Run:
emergencyStop = false;
if(powerOn)
restoreSpeed();
break;
default:
break;
}
}
catch(...)
{
}
}
const std::shared_ptr<::Decoder>& CommandStation::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const
{
auto it = std::find_if(decoders->begin(), decoders->end(), [=](auto& decoder){ return decoder->protocol.value() == protocol && decoder->address.value() == address && decoder->longAddress == longAddress; });
if(it != decoders->end())
return *it;
return Decoder::null;
}
void CommandStation::emergencyStopChanged(bool value)
{
for(auto& controller : *controllers)
controller->emergencyStopChanged(value);
}
void CommandStation::powerOnChanged(bool value)
{
for(auto& controller : *controllers)
controller->powerOnChanged(value);
}
void CommandStation::checkAllDecoders() const
{
for(const auto& decoder : *decoders)
checkDecoder(*decoder);
}
void CommandStation::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
for(auto& controller : *controllers)
controller->decoderChanged(decoder, changes, functionNumber);
}
//! \brief restore speed of all decoders that are not (emergency) stopped
void CommandStation::restoreSpeed()
{
for(const auto& decoder : *decoders)
if(!decoder->emergencyStop && !almostZero(decoder->throttle.value()))
decoderChanged(*decoder, DecoderChangeFlags::Throttle, 0);
}

Datei anzeigen

@ -1,68 +0,0 @@
/**
* server/src/hardware/commandstation/commandstation.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_COMMANDSTATION_COMMANDSTATION_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATION_HPP
#include "../../core/idobject.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/commandstationstatus.hpp"
#include "../decoder/decoderlist.hpp"
#include "../controller/controllerlist.hpp"
class Decoder;
enum class DecoderChangeFlags;
class CommandStation : public IdObject
{
friend class ::Decoder;
protected:
void loaded() override;
void addToWorld() final;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
virtual bool setOnline(bool& value) = 0;
virtual void emergencyStopChanged(bool value);
virtual void powerOnChanged(bool value);
void checkAllDecoders() const;
virtual void checkDecoder(const Decoder& /*decoder*/) const {}
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
void restoreSpeed();
public:
CommandStation(const std::weak_ptr<World>& world, std::string_view _id);
Property<std::string> name;
Property<bool> online;
Property<bool> emergencyStop;
Property<bool> powerOn;
ObjectProperty<DecoderList> decoders;
ObjectProperty<ControllerList> controllers;
Property<std::string> notes;
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress = false) const;
};
#endif

Datei anzeigen

@ -1,46 +0,0 @@
/**
* server/src/hardware/commandstation/commandstationlist.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_HARDWARE_COMMANDSTATION_COMMANDSTATIONLIST_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLIST_HPP
#include "../../core/objectlist.hpp"
#include "commandstation.hpp"
class CommandStationList : public ObjectList<CommandStation>
{
protected:
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("command_station_list")
Method<std::shared_ptr<CommandStation>(std::string_view)> add;
Method<void(const std::shared_ptr<CommandStation>&)> remove;
CommandStationList(Object& _parent, const std::string& parentPropertyName);
TableModelPtr getModel() final;
};
#endif

Datei anzeigen

@ -1,100 +0,0 @@
/**
* server/src/core/
*
* 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 "commandstationlisttablemodel.hpp"
#include "commandstationlist.hpp"
#include "../../utils/utf8.hpp"
#include "../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnOnline = 2;
constexpr uint32_t columnEmergencyStop = 3;
constexpr uint32_t columnTrackPower = 4;
bool CommandStationListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name" ||
name == "online" ||
name == "emergency_stop" ||
name == "power_on";
}
CommandStationListTableModel::CommandStationListTableModel(CommandStationList& list) :
ObjectListTableModel<CommandStation>(list)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::CommandStation::online,
DisplayName::CommandStation::emergencyStop,
DisplayName::CommandStation::powerOn,
});
}
std::string CommandStationListTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const CommandStation& cs = getItem(row);
switch(column)
{
case columnId:
return cs.id;
case columnName:
return cs.name;
case columnOnline:
return cs.online ? "\u2022" : "";
case columnEmergencyStop:
return cs.emergencyStop ? "\u2022" : "";
case columnTrackPower:
return cs.powerOn ? UTF8_CHECKMARK : UTF8_BALLOT_X;
default:
assert(false);
break;
}
}
return "";
}
void CommandStationListTableModel::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() == "online")
changed(row, columnOnline);
else if(property.name() == "emergency_stop")
changed(row, columnEmergencyStop);
else if(property.name() == "track_voltage_off")
changed(row, columnTrackPower);
}

Datei anzeigen

@ -1,48 +0,0 @@
/**
* server/src/hardware/commandstation/commandstationlisttablemodel.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_COMMANDSTATION_COMMANDSTATIONLISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONLISTTABLEMODEL_HPP
#include "../../core/objectlisttablemodel.hpp"
#include "commandstationlist.hpp"
class CommandStationList;
class CommandStationListTableModel : public ObjectListTableModel<CommandStation>
{
friend class CommandStationList;
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;
public:
CLASS_ID("command_station_list_table_model")
static bool isListedProperty(const std::string& name);
CommandStationListTableModel(CommandStationList& commandStationList);
std::string getText(uint32_t column, uint32_t row) const final;
};
#endif

Datei anzeigen

@ -1,45 +0,0 @@
/**
* server/src/hardware/commandstation/commandstations.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 "commandstations.hpp"
std::shared_ptr<CommandStation> CommandStations::create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id)
{
if(classId == DCCPlusPlusSerial::classId)
return DCCPlusPlusSerial::create(world, id);
if(classId == LocoNetSerial::classId)
return LocoNetSerial::create(world, id);
else if(classId == LocoNetTCPBinary::classId)
return LocoNetTCPBinary::create(world, id);
#ifdef USB_XPRESSNET
else if(classId == USBXpressNetInterface::classId)
return USBXpressNetInterface::create(world, id);
#endif
else if(classId == XpressNetSerial::classId)
return XpressNetSerial::create(world, id);
else if(classId == RocoZ21::classId)
return RocoZ21::create(world, id);
else if(classId == VirtualCommandStation::classId)
return VirtualCommandStation::create(world, id);
else
return std::shared_ptr<CommandStation>();
}

Datei anzeigen

@ -1,58 +0,0 @@
/**
* server/src/hardware/commandstation/create.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_COMMANDSTATION_COMMANDSTATIONS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_COMMANDSTATIONS_HPP
#include "commandstation.hpp"
#include "../../utils/makearray.hpp"
#include "dccplusplusserial.hpp"
#include "loconetserial.hpp"
#include "loconettcpbinary.hpp"
#ifdef USB_XPRESSNET
#include "usbxpressnetinterface.hpp"
#endif
#include "xpressnetserial.hpp"
#include "rocoz21.hpp"
#include "virtualcommandstation.hpp"
struct CommandStations
{
static constexpr std::string_view classIdPrefix = "command_station.";
static constexpr auto classList = makeArray(
DCCPlusPlusSerial::classId,
LocoNetSerial::classId,
LocoNetTCPBinary::classId,
#ifdef USB_XPRESSNET
USBXpressNetInterface::classId,
#endif
XpressNetSerial::classId,
RocoZ21::classId,
VirtualCommandStation::classId
);
static std::shared_ptr<CommandStation> create(const std::weak_ptr<World>& world, std::string_view classId, std::string_view id);
};
#endif

Datei anzeigen

@ -1,133 +0,0 @@
/**
* server/src/hardware/commandstation/dccplusplusserial.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 "dccplusplusserial.hpp"
#include "../protocol/dccplusplus/messages.hpp"
#include "../../core/eventloop.hpp"
#include "../../log/log.hpp"
DCCPlusPlusSerial::DCCPlusPlusSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
dccPlusPlus{this, "dcc_plus_plus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "DCC++ (serial)";
baudrate = 115200;
dccPlusPlus.setValueInternal(std::make_shared<DCCPlusPlus::DCCPlusPlus>(*this, dccPlusPlus.name(), std::bind(&DCCPlusPlusSerial::send, this, std::placeholders::_1)));
m_interfaceItems.insertBefore(dccPlusPlus, notes);
}
void DCCPlusPlusSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
dccPlusPlus->emergencyStopChanged(value);
}
void DCCPlusPlusSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
dccPlusPlus->powerOnChanged(value);
}
void DCCPlusPlusSerial::checkDecoder(const Decoder& decoder) const
{
CommandStation::checkDecoder(decoder);
dccPlusPlus->checkDecoder(decoder);
}
void DCCPlusPlusSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
dccPlusPlus->decoderChanged(decoder, changes, functionNumber);
}
bool DCCPlusPlusSerial::send(std::string_view message)
{
if(!m_serialPort.is_open())
return false;
// for now a blocking implementation, will be replaced by async.
boost::system::error_code ec;
size_t todo = message.size();
while(todo > 0)
{
todo -= m_serialPort.write_some(boost::asio::buffer(message.data(), message.size()), ec);
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
}
return true;
}
void DCCPlusPlusSerial::started()
{
send(DCCPlusPlus::Ex::setSpeedSteps(dccPlusPlus->speedSteps));
}
void DCCPlusPlusSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const char* pos = reinterpret_cast<const char*>(m_readBuffer.data());
bytesTransferred += m_readBufferOffset;
size_t i = 0;
while(i < bytesTransferred)
{
if(*(pos + i) == '\n')
{
dccPlusPlus->receive(std::string_view{pos, i + 1});
pos += i + 1;
bytesTransferred -= i + 1;
i = 0;
}
else
i++;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}

Datei anzeigen

@ -1,162 +0,0 @@
/**
* hardware/commandstation/loconetserial.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "loconetserial.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
interface{this, "interface", LocoNetSerialInterface::Custom, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](LocoNetSerialInterface value)
{
switch(value)
{
case LocoNetSerialInterface::Custom:
break;
case LocoNetSerialInterface::DigikeijsDR5000:
baudrate = 115200;
flowControl = SerialFlowControl::Hardware;
break;
case LocoNetSerialInterface::RoSoftLocoNetInterface:
baudrate = 19200;
flowControl = SerialFlowControl::Hardware;
break;
}
}},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "LocoNet (serial)";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
Attributes::addEnabled(interface, !online);
Attributes::addValues(interface, LocoNetSerialInterfaceValues);
m_interfaceItems.insertBefore(interface, baudrate);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
}
void LocoNetSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
loconet->emergencyStopChanged(value);
}
void LocoNetSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
loconet->powerOnChanged(value);
}
void LocoNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
loconet->decoderChanged(decoder, changes, functionNumber);
}
bool LocoNetSerial::send(const LocoNet::Message& message)
{
if(!m_serialPort.is_open())
return false;
boost::system::error_code ec;
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&message), message.size()), ec); // TODO async
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
return true;
}
void LocoNetSerial::started()
{
loconet->queryLocoSlots();
}
void LocoNetSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const LocoNet::Message* message = reinterpret_cast<const LocoNet::Message*>(pos);
size_t drop = 0;
while((message->size() == 0 || (message->size() <= bytesTransferred && !LocoNet::isValid(*message))) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const LocoNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
else if(message->size() <= bytesTransferred)
{
loconet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}

Datei anzeigen

@ -1,52 +0,0 @@
/**
* server/src/hardware/commandstation/loconetserial.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_COMMANDSTATION_LOCONETSERIAL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
#include "serialcommandstation.hpp"
#include "../protocol/loconet/loconet.hpp"
#include "../../enum/loconetserialinterface.hpp"
class LocoNetSerial : public SerialCommandStation
{
protected:
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool start();
bool send(const LocoNet::Message& msg);
void started() final;
void read() final;
public:
CLASS_ID("command_station.loconet_serial")
CREATE(LocoNetSerial)
Property<LocoNetSerialInterface> interface;
ObjectProperty<LocoNet::LocoNet> loconet;
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,192 +0,0 @@
/**
* hardware/commandstation/loconettcpbinary.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2021 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "loconettcpbinary.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetTCPBinary::LocoNetTCPBinary(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_socket{Traintastic::instance->ioContext()},
hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
port{this, "port", 5550, PropertyFlags::ReadWrite | PropertyFlags::Store},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "LocoNet (TCP binary)";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(), std::bind(&LocoNetTCPBinary::send, this, std::placeholders::_1)));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, true);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, true);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
}
bool LocoNetTCPBinary::setOnline(bool& value)
{
if(!m_socket.is_open() && value)
{
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint endpoint;
endpoint.port(port);
endpoint.address(boost::asio::ip::make_address(hostname, ec));
if(ec)
{
Log::log(*this, LogMessage::E2003_MAKE_ADDRESS_FAILED_X, ec);
return false;
}
m_socket.connect(endpoint, ec);
if(ec)
{
Log::log(*this, LogMessage::E2005_SOCKET_CONNECT_FAILED_X, ec);
return false;
}
m_socket.set_option(boost::asio::socket_base::linger(true, 0));
m_socket.set_option(boost::asio::ip::tcp::no_delay(true));
receive(); // start receiving messages
Attributes::setEnabled(hostname, false);
Attributes::setEnabled(port, false);
}
else if(m_socket.is_open() && !value)
{
Attributes::setEnabled(hostname, true);
Attributes::setEnabled(port, true);
m_socket.close();
}
return true;
}
void LocoNetTCPBinary::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
loconet->emergencyStopChanged(value);
}
void LocoNetTCPBinary::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
loconet->powerOnChanged(value);
}
void LocoNetTCPBinary::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
loconet->decoderChanged(decoder, changes, functionNumber);
}
void LocoNetTCPBinary::receive()
{
m_socket.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const LocoNet::Message* message = reinterpret_cast<const LocoNet::Message*>(pos);
size_t drop = 0;
while((message->size() == 0 || (message->size() <= bytesTransferred && !LocoNet::isValid(*message))) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const LocoNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
else if(message->size() <= bytesTransferred)
{
loconet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
receive();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2008_SOCKET_READ_FAILED_X, ec);
online = false;
});
});
}
bool LocoNetTCPBinary::send(const LocoNet::Message& message)
{
if(!m_socket.is_open())
return false;
// for now a blocking implementation, will be replaced by async.
boost::system::error_code ec;
size_t todo = message.size();
while(todo > 0)
{
todo -= m_socket.write_some(boost::asio::buffer(static_cast<const void*>(&message), message.size()), ec);
if(ec)
{
Log::log(*this, LogMessage::E2007_SOCKET_WRITE_FAILED_X, ec);
return false;
}
}
return true;
}

Datei anzeigen

@ -1,56 +0,0 @@
/**
* server/src/hardware/commandstation/loconettcpbinary.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_COMMANDSTATION_LOCONETTCPBINARY_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETTCPBINARY_HPP
#include "commandstation.hpp"
#include <boost/asio.hpp>
#include "../protocol/loconet/loconet.hpp"
class LocoNetTCPBinary : public CommandStation
{
protected:
boost::asio::ip::tcp::socket m_socket;
std::array<uint8_t, 1024> m_readBuffer;
uint16_t m_readBufferOffset;
bool setOnline(bool& value) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
void receive();
bool send(const LocoNet::Message& msg);
public:
CLASS_ID("command_station.loconet_tcp_binary")
CREATE(LocoNetTCPBinary)
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<LocoNet::LocoNet> loconet;
LocoNetTCPBinary(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,524 +0,0 @@
/**
* server/src/hardware/commandstation/rocoz21.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "rocoz21.hpp"
#include "../../core/traintastic.hpp"
#include "../../world/world.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../decoder/decoderchangeflags.hpp"
#include "../protocol/xpressnet/messages.hpp"
#include "../protocol/z21/messages.hpp"
#include "../../utils/tohex.hpp"
#include "../../utils/category.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
#define SET_ADDRESS \
if(decoder.longAddress) \
{ \
cmd.addressHigh = 0xc0 | (decoder.address >> 8); \
cmd.addressLow = decoder.address & 0xff; \
} \
else \
{ \
cmd.addressHigh = 0; \
cmd.addressLow = decoder.address; \
}
RocoZ21::RocoZ21(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_socket{Traintastic::instance->ioContext()},
hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store},
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
serialNumber{this, "serial_number", "", PropertyFlags::ReadOnly},
hardwareType{this, "hardware_type", "", PropertyFlags::ReadOnly},
firmwareVersion{this, "firmware_version", "", PropertyFlags::ReadOnly},
mainCurrent{this, "main_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
progCurrent{this, "prog_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
filteredMainCurrent{this, "filtered_main_current", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
temperature{this, "temperature", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
supplyVoltage{this, "supply_voltage", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
vccVoltage{this, "vcc_voltage", std::numeric_limits<float>::quiet_NaN(), PropertyFlags::ReadOnly},
shortCircuit{this, "short_circuit", false, PropertyFlags::ReadOnly},
programmingModeActive{this, "programming_mode_active", false, PropertyFlags::ReadOnly},
highTemperature{this, "high_temperature", false, PropertyFlags::ReadOnly},
powerLost{this, "power_lost", false, PropertyFlags::ReadOnly},
shortCircutInternal{this, "short_circut_internal", false, PropertyFlags::ReadOnly},
shortCircutExternal{this, "short_circut_external", false, PropertyFlags::ReadOnly}
{
name = "Z21";
loconet.setValueInternal(LocoNet::LocoNet::create(*this, loconet.name(),
[/*this*/](const ::LocoNet::Message& /*msg*/)
{
return false;
}));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, true);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, true);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
Attributes::addCategory(serialNumber, Category::info);
m_interfaceItems.insertBefore(serialNumber, notes);
Attributes::addCategory(hardwareType, Category::info);
m_interfaceItems.insertBefore(hardwareType, notes);
Attributes::addCategory(firmwareVersion, Category::info);
m_interfaceItems.insertBefore(firmwareVersion, notes);
Attributes::addCategory(mainCurrent, Category::info);
m_interfaceItems.insertBefore(mainCurrent, notes);
Attributes::addCategory(progCurrent, Category::info);
m_interfaceItems.insertBefore(progCurrent, notes);
Attributes::addCategory(filteredMainCurrent, Category::info);
m_interfaceItems.insertBefore(filteredMainCurrent, notes);
Attributes::addCategory(temperature, Category::info);
m_interfaceItems.insertBefore(temperature, notes);
Attributes::addCategory(supplyVoltage, Category::info);
m_interfaceItems.insertBefore(supplyVoltage, notes);
Attributes::addCategory(vccVoltage, Category::info);
m_interfaceItems.insertBefore(vccVoltage, notes);
Attributes::addCategory(shortCircuit, Category::info);
m_interfaceItems.insertBefore(shortCircuit, notes);
Attributes::addCategory(programmingModeActive, Category::info);
m_interfaceItems.insertBefore(programmingModeActive, notes);
Attributes::addCategory(highTemperature, Category::info);
m_interfaceItems.insertBefore(highTemperature, notes);
Attributes::addCategory(powerLost, Category::info);
m_interfaceItems.insertBefore(powerLost, notes);
Attributes::addCategory(shortCircutInternal, Category::info);
m_interfaceItems.insertBefore(shortCircutInternal, notes);
Attributes::addCategory(shortCircutExternal, Category::info);
m_interfaceItems.insertBefore(shortCircutExternal, notes);
}
void RocoZ21::emergencyStopChanged(bool value)
{
if(online && value)
send(Z21::LanXSetStop());
}
void RocoZ21::powerOnChanged(bool value)
{
if(online)
{
if(value)
send(Z21::LanXSetTrackPowerOn());
else
send(Z21::LanXSetTrackPowerOff());
}
}
/*
bool RocoZ21::isDecoderSupported(Decoder& decoder) const
{
return
decoder.protocol == DecoderProtocol::DCC &&
decoder.address >= 1 &&
decoder.address <= (decoder.longAddress ? 9999 : 127);
}
*/
void RocoZ21::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::Throttle | DecoderChangeFlags::SpeedSteps))
{
Z21::LanXSetLocoDrive cmd;
//cmd.dataLen = sizeof(cmd);
//cmd.header = Z21::LAN_X;
SET_ADDRESS;
switch(decoder.speedSteps)
{
case 14:
{
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 14);
cmd.db0 = 0x10;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
cmd.speedAndDirection = speedStep + 1;
break;
}
case 28:
{
uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 28);
cmd.db0 = 0x12;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
{
speedStep++;
cmd.speedAndDirection = ((speedStep & 0x01) << 4) | (speedStep >> 1);
}
break;
}
case 126:
case 128:
default:
{
const uint8_t speedStep = Decoder::throttleToSpeedStep(decoder.throttle, 126);
cmd.db0 = 0x13;
if(decoder.emergencyStop)
cmd.speedAndDirection = 0x01;
else if(speedStep > 0)
cmd.speedAndDirection = speedStep + 1;
break;
}
}
if(decoder.direction.value() == Direction::Forward)
cmd.speedAndDirection |= 0x80;
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&cmd.xheader));
send(cmd);
}
else if(has(changes, DecoderChangeFlags::FunctionValue))
{
if(functionNumber <= 28)
{
if(const auto& f = decoder.getFunction(functionNumber))
{
Z21::LanXSetLocoFunction cmd;
//cmd.dataLen = sizeof(cmd);
//cmd.header = Z21_LAN_X;
SET_ADDRESS;
cmd.db3 = (f->value ? 0x40 : 0x00) | static_cast<uint8_t>(functionNumber);
cmd.checksum = XpressNet::calcChecksum(*reinterpret_cast<const XpressNet::Message*>(&cmd.xheader));
send(cmd);
}
}
}
}
bool RocoZ21::setOnline(bool& value)
{
if(!m_socket.is_open() && value)
{
boost::system::error_code ec;
m_remoteEndpoint.port(port);
m_remoteEndpoint.address(boost::asio::ip::make_address(hostname, ec));
if(ec)
{
Log::log(*this, LogMessage::E2003_MAKE_ADDRESS_FAILED_X, ec);
return false;
}
if(m_socket.open(boost::asio::ip::udp::v4(), ec))
{
Log::log(*this, LogMessage::E2004_SOCKET_OPEN_FAILED_X, ec);
return false;
}
else if(m_socket.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), port), ec))
{
m_socket.close();
Log::log(*this, LogMessage::E2006_SOCKET_BIND_FAILED_X, ec);
return false;
}
receive(); // start receiving messages
send(Z21::LanSetBroadcastFlags(0x07000000 | /*0x00010000 |*/ 0x00000100 | 0x00000001));
// try to communicate with Z21
send(Z21::LanGetSerialNumber());
send(Z21::LanGetHardwareInfo());
send(Z21::LanGetBroadcastFlags());
send(Z21::LanSystemStateGetData());
for(auto& decoder : *decoders)
send(Z21::LanXGetLocoInfo(decoder->address, decoder->longAddress));
Attributes::setEnabled(hostname, false);
Attributes::setEnabled(port, false);
}
else if(m_socket.is_open() && !value)
{
send(Z21::LanLogoff());
serialNumber.setValueInternal("");
hardwareType.setValueInternal("");
firmwareVersion.setValueInternal("");
Attributes::setEnabled(hostname, true);
Attributes::setEnabled(port, true);
m_socket.close();
}
return true;
}
void RocoZ21::receive()
{
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_receiveEndpoint,
[this](const boost::system::error_code& ec, std::size_t bytesReceived)
{
if(!ec)
{
if((bytesReceived >= sizeof(Z21::Message)))
{
const Z21::Message* message = reinterpret_cast<const Z21::Message*>(m_receiveBuffer.data());
//const z21_lan_header* cmd = reinterpret_cast<const z21_lan_header*>(m_receiveBuffer.data());
switch(message->header())
{
case Z21::LAN_GET_SERIAL_NUMBER:
if(message->dataLen() == sizeof(Z21::LanGetSerialNumberReply))
{
EventLoop::call(
[this, value=std::to_string(static_cast<const Z21::LanGetSerialNumberReply*>(message)->serialNumber())]()
{
serialNumber.setValueInternal(value);
});
}
break;
case Z21::LAN_GET_HWINFO:
{
const Z21::LanGetHardwareInfoReply* reply = static_cast<const Z21::LanGetHardwareInfoReply*>(message);
std::string hwType;
switch(reply->hardwareType())
{
case Z21::HWT_Z21_OLD:
hwType = "Black Z21 (hardware variant from 2012)";
break;
case Z21::HWT_Z21_NEW:
hwType = "Black Z21 (hardware variant from 2013)";
break;
case Z21::HWT_SMARTRAIL:
hwType = "SmartRail (from 2012)";
break;
case Z21::HWT_Z21_SMALL:
hwType = "White Z21 (starter set variant from 2013)";
break;
case Z21::HWT_Z21_START :
hwType = "Z21 start (starter set variant from 2016)";
break;
default:
hwType = "0x" + toHex(reply->hardwareType());
break;
}
const std::string fwVersion = std::to_string(reply->firmwareVersionMajor()) + "." + std::to_string(reply->firmwareVersionMinor());
EventLoop::call(
[this, hwType, fwVersion]()
{
hardwareType.setValueInternal(hwType);
firmwareVersion.setValueInternal(fwVersion);
});
break;
}
case Z21::LAN_X:
{
// TODO check XOR
const uint8_t xheader = static_cast<const Z21::LanX*>(message)->xheader;
switch(xheader)
{
case Z21::LAN_X_LOCO_INFO:
{
const Z21::LanXLocoInfo* info = static_cast<const Z21::LanXLocoInfo*>(message);
const uint16_t address = (static_cast<uint16_t>(info->addressHigh) << 8) | info->addressLow;
const uint8_t speedStepMode = info->db2 & 0x07;
const Direction direction = (info->speedAndDirection & 0x80) ? Direction::Forward : Direction::Reverse;
const uint8_t speedStep = info->speedAndDirection & 0x7f;
const uint32_t functions =
((info->db4 & 0x10) >> 4) |
((info->db4 & 0x0f) << 1) |
(static_cast<uint32_t>(info->f5f12) << 5) |
(static_cast<uint32_t>(info->f13f20) << 13) |
(static_cast<uint32_t>(info->f21f28) << 21);
EventLoop::call(
[this, address, speedStepMode, direction, speedStep, functions]()
{
const std::shared_ptr<Decoder>& decoder = getDecoder(DecoderProtocol::DCC, address & 0x3fff, address & 0xc000);
if(decoder)
{
decoder->direction = direction;
switch(speedStepMode)
{
case 0:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 14));
break;
case 2:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 28));
break;
case 4:
decoder->throttle.setValueInternal(Decoder::speedStepToThrottle(speedStep, 126));
break;
}
for(auto& function : *decoder->functions)
{
const uint8_t number = function->number;
if(number <= 28)
function->value.setValueInternal(functions & (1 << number));
}
}
});
break;
}
case Z21::LAN_X_BC:
{
break;
}
case Z21::LAN_X_BC_STOPPED:
EventLoop::call(
[this]()
{
emergencyStop.setValueInternal(true);
});
break;
default:
EventLoop::call([this, xheader](){ Log::log(*this, LogMessage::D2003_UNKNOWN_XHEADER_0XX, toHex(xheader)); });
break;
}
break;
}
case Z21::LAN_SYSTEMSTATE_DATACHANGED:
{
/*
const Z21::LanSystemStateDataChanged state = *reinterpret_cast<const Z21::LanSystemStateDataChanged*>(m_receiveBuffer.data());
EventLoop::call(
[this, state]()
{
mainCurrent.setValueInternal(state.mainCurrent / 1e3); //!< Current on the main track in mA
progCurrent.setValueInternal(state.progCurrent / 1e3); //!< Current on programming track in mA;
filteredMainCurrent.setValueInternal(state.filteredMainCurrent / 1e3); //!< Smoothed current on the main track in mA
temperature.setValueInternal(state.temperature); //!< Command station internal temperature in °C
supplyVoltage.setValueInternal(state.supplyVoltage / 1e3); //!< Supply voltage in mV
vccVoltage.setValueInternal(state.vccVoltage / 1e3); //!< Internal voltage, identical to track voltage in mV
emergencyStop.setValueInternal(state.centralState & Z21_CENTRALSTATE_EMERGENCYSTOP);
trackVoltageOff.setValueInternal(state.centralState & Z21_CENTRALSTATE_TRACKVOLTAGEOFF);
shortCircuit.setValueInternal(state.centralState & Z21_CENTRALSTATE_SHORTCIRCUIT);
programmingModeActive.setValueInternal(state.centralState & Z21_CENTRALSTATE_PROGRAMMINGMODEACTIVE);
highTemperature.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_HIGHTEMPERATURE);
powerLost.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_POWERLOST);
shortCircutInternal.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_SHORTCIRCUITEXTERNAL);
shortCircutExternal.setValueInternal(state.centralStateEx & Z21_CENTRALSTATEEX_SHORTCIRCUITINTERNAL);
});
*/
break;
}
case Z21::LAN_LOCONET_Z21_RX:
//case Z21_LAN_LOCONET_Z21_TX:
//case Z21_LAN_LOCONET_Z21_LAN:
loconet->receive(*reinterpret_cast<const ::LocoNet::Message*>(m_receiveBuffer.data() + sizeof(Z21::Message)));
break;
/*
using LocoNet = LocoNet;
const LocoNet::Header* message = reinterpret_cast<const LocoNet::Header*>(m_receiveBuffer.data() + sizeof(z21_lan_header));
switch(message->opCode)
{
case LocoNet::OPC_INPUT_REP:
{
const LocoNet::InputRep* inputRep = static_cast<const LocoNet::InputRep*>(message);
//if(debugEnabled)
{
const std::string message = "loconet rx OPC_INPUT_REP:"
" address=" + std::to_string(inputRep->address()) +
" input=" + (inputRep->isAuxInput() ? "aux" : "switch") +
" value=" + (inputRep->value() ? "high" : "low");
}
break;
}
default:
//if(debugEnabled)
{
std::string message = "unknown loconet message: ";
for(int i = 4; i < cmd->dataLen; i++)
message += toHex(reinterpret_cast<const uint8_t*>(cmd)[i]);
}
break;
}
*/
default:
//if(debugEnabled)
{
std::string data = "dataLen=0x" + toHex(message->dataLen()) + ", header=0x" + toHex(message->header());
if(message->dataLen() > 4)
{
data += ", data=";
for(int i = sizeof(Z21::Message); i < message->dataLen(); i++)
data += toHex(reinterpret_cast<const uint8_t*>(message)[i]);
}
EventLoop::call([this, data](){ Log::log(*this, LogMessage::D2006_UNKNOWN_MESSAGE_X, data); });
}
break;
}
}
receive();
}
else
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2009_SOCKET_RECEIVE_FAILED_X, ec); });
});
}
void RocoZ21::send(const Z21::Message& message)
{
// TODO async
// TODO: add to queue, send async
boost::system::error_code ec;
m_socket.send_to(boost::asio::buffer(&message, message.dataLen()), m_remoteEndpoint, 0, ec);
if(ec)
Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec);
/*
m_socket.send_to(boost::asio:buffer(&message, message.dataLen()), 0, m_remoteEndpoint);,
[this](const boost::system::error_code& ec, std::size_t)
{
if(ec)
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec); });
});*/
}

Datei anzeigen

@ -1,84 +0,0 @@
/**
* server/src/hardware/commandstation/rocoz21.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_COMMANDSTATION_ROCOZ21_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_ROCOZ21_HPP
#include "commandstation.hpp"
#include <boost/asio.hpp>
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet/loconet.hpp"
struct z21_lan_header;
namespace Z21 {
class Message;
}
class RocoZ21 : public CommandStation
{
protected:
boost::asio::ip::udp::socket m_socket;
boost::asio::ip::udp::endpoint m_remoteEndpoint;
boost::asio::ip::udp::endpoint m_receiveEndpoint;
std::array<uint8_t,64> m_receiveBuffer;
bool setOnline(bool& value) final;
//bool isDecoderSupported(Decoder& decoder) const final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
void receive();
void send(const Z21::Message& message);
void send(const z21_lan_header* msg);
inline void send(const z21_lan_header& msg) { send(&msg); }
public:
CLASS_ID("command_station.z21")
CREATE(RocoZ21)
RocoZ21(const std::weak_ptr<World>& world, std::string_view _id);
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<LocoNet::LocoNet> loconet;
Property<std::string> serialNumber;
Property<std::string> hardwareType;
Property<std::string> firmwareVersion;
Property<float> mainCurrent;
Property<float> progCurrent;
Property<float> filteredMainCurrent;
Property<float> temperature;
Property<float> supplyVoltage;
Property<float> vccVoltage;
//Property<bool> emergencyStop;
//Property<bool> trackVoltageOff;
Property<bool> shortCircuit;
Property<bool> programmingModeActive;
Property<bool> highTemperature;
Property<bool> powerLost;
Property<bool> shortCircutInternal;
Property<bool> shortCircutExternal;
};
#endif

Datei anzeigen

@ -1,133 +0,0 @@
/**
* server/src/hardware/commandstation/serialcommandstation.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.
*/
#include "serialcommandstation.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
SerialCommandStation::SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_serialPort{Traintastic::instance->ioContext()},
m_readBufferOffset{0},
port{this, "port", "/dev/ttyUSB0", PropertyFlags::ReadWrite | PropertyFlags::Store},
baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](uint32_t)
{
//interface = LocoNetSerialInterface::Custom;
}},
flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](SerialFlowControl)
{
//interface = LocoNetSerialInterface::Custom;
}}
{
Attributes::addDisplayName(port, DisplayName::Serial::port);
Attributes::addEnabled(port, !online);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(baudrate, DisplayName::Serial::baudrate);
Attributes::addEnabled(baudrate, !online);
m_interfaceItems.insertBefore(baudrate, notes);
Attributes::addDisplayName(flowControl, DisplayName::Serial::flowControl);
Attributes::addEnabled(flowControl, !online);
Attributes::addValues(flowControl, SerialFlowControlValues);
m_interfaceItems.insertBefore(flowControl, notes);
}
void SerialCommandStation::loaded()
{
CommandStation::loaded();
updateEnabled();
}
void SerialCommandStation::worldEvent(WorldState state, WorldEvent event)
{
CommandStation::worldEvent(state, event);
if(event == WorldEvent::EditEnabled || event == WorldEvent::EditDisabled)
updateEnabled();
}
bool SerialCommandStation::setOnline(bool& value)
{
if(!m_serialPort.is_open() && value)
{
if(!start())
{
value = false;
return false;
}
m_readBufferOffset = 0;
read();
started();
}
else if(m_serialPort.is_open() && !value)
stop();
return true;
}
bool SerialCommandStation::start()
{
boost::system::error_code ec;
m_serialPort.open(port, ec);
if(ec)
{
Log::log(*this, LogMessage::E2010_SERIAL_PORT_OPEN_FAILED_X, ec);
return false;
}
m_serialPort.set_option(boost::asio::serial_port_base::baud_rate(baudrate));
m_serialPort.set_option(boost::asio::serial_port_base::character_size(8));
m_serialPort.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
m_serialPort.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
switch(flowControl)
{
case SerialFlowControl::None:
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));
break;
case SerialFlowControl::Hardware:
m_serialPort.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware));
break;
}
return true;
}
void SerialCommandStation::stop()
{
// TODO: send power off cmd??
m_serialPort.close();
}
void SerialCommandStation::updateEnabled()
{
auto w = m_world.lock();
bool enabled = w && contains(w->state.value(), WorldState::Edit) && !online;
Attributes::setEnabled(port, enabled);
Attributes::setEnabled(baudrate, enabled);
Attributes::setEnabled(flowControl, enabled);
}

Datei anzeigen

@ -1,59 +0,0 @@
/**
* server/src/hardware/commandstation/serialcommandstation.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_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_SERIALCOMMANDSTATION_HPP
#include "commandstation.hpp"
#include "../../enum/serialflowcontrol.hpp"
#include <boost/asio/serial_port.hpp>
class SerialCommandStation : public CommandStation
{
private:
void updateEnabled();
protected:
boost::asio::serial_port m_serialPort;
std::array<uint8_t, 1024> m_readBuffer;
uint16_t m_readBufferOffset;
void loaded() override;
void worldEvent(WorldState state, WorldEvent event) override;
bool setOnline(bool& value) final;
bool start();
virtual void stop();
virtual void started() {}
virtual void read() = 0;
public:
Property<std::string> port;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
SerialCommandStation(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,112 +0,0 @@
/**
* server/src/hardware/commandstation/usbxpressnetinterface.cpp
*
* Copyright (C) 2019-2021 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "usbxpressnetinterface.hpp"
#include "../../core/traintastic.hpp"
#include "../../world/world.hpp"
USBXpressNetInterface::USBXpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id) :
CommandStation(world, _id),
m_handle{nullptr},
serial{this, "serial", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
address{this, "address", 31, PropertyFlags::ReadWrite | PropertyFlags::Store},
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "USB XpressNet interface";
xpressnet.setValueInternal(std::make_shared<XpressNet::XpressNet>(*this, xpressnet.name(), std::bind(&USBXpressNetInterface::send, this, std::placeholders::_1)));
m_interfaceItems.insertBefore(serial, notes);
m_interfaceItems.insertBefore(address, notes);
m_interfaceItems.insertBefore(xpressnet, notes);
usbxpressnet_init();
}
USBXpressNetInterface::~USBXpressNetInterface()
{
if(m_handle)
{
usbxpressnet_reset(m_handle);
usbxpressnet_close(m_handle);
}
usbxpressnet_fini();
}
bool USBXpressNetInterface::setOnline(bool& value)
{
if(!m_handle && value)
{
usbxpressnet_status status = usbxpressnet_open(!serial.value().empty() ? serial.value().c_str() : nullptr, &m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_open: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_reset(m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_reset: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_set_mode(m_handle, USBXPRESSNET_MODE_DEVICE, address);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_set_mode: ") + usbxpressnet_status_str(status));
return false;
}
}
else if(m_handle && !value)
{
usbxpressnet_close(m_handle);
m_handle = nullptr;
}
return true;
}
void USBXpressNetInterface::emergencyStopChanged(bool value)
{
}
void USBXpressNetInterface::powerOnChanged(bool value)
{
}
void USBXpressNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(online)
xpressnet->decoderChanged(decoder, changes, functionNumber);
}
bool USBXpressNetInterface::send(const XpressNet::Message& msg)
{
assert(XpressNet::isChecksumValid(msg));
if(!m_handle)
return false;
usbxpressnet_status status;
if((status = usbxpressnet_send_message(m_handle, &msg)) != USBXPRESSNET_STATUS_SUCCESS)
{
Traintastic::instance->console->critical(id, std::string("usbxpressnet_send_message: ") + usbxpressnet_status_str(status));
return false;
}
return true;
}

Datei anzeigen

@ -1,57 +0,0 @@
/**
* server/src/hardware/commandstation/usbxpressnetinterface.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_COMMANDSTATION_USBXPRESSNETINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_USBXPRESSNETINTERFACE_HPP
#include "commandstation.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
#include <usbxpressnet.h>
class USBXpressNetInterface : public CommandStation
{
protected:
static const uint8_t addressMin = 1;
static const uint8_t addressMax = 31;
usbxpressnet_handle m_handle;
bool setOnline(bool& value) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool send(const XpressNet::Message& msg);
public:
CLASS_ID("command_station.usb_xpressnet_interface")
CREATE(USBXpressNetInterface)
Property<std::string> serial;
Property<uint8_t> address;
ObjectProperty<XpressNet::XpressNet> xpressnet;
USBXpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id);
~USBXpressNetInterface() final;
};
#endif

Datei anzeigen

@ -1,273 +0,0 @@
/**
* hardware/commandstation/xpressnetserial.cpp
*
* This file is part of the traintastic source code
*
* Copyright (C) 2019-2021 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "xpressnetserial.hpp"
#include "../../world/world.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
XpressNetSerial::XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
SerialCommandStation(world, _id),
interface{this, "interface", XpressNetSerialInterface::LenzLI100, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](XpressNetSerialInterface value)
{
switch(value)
{
case XpressNetSerialInterface::Custom:
break;
case XpressNetSerialInterface::LenzLI100:
case XpressNetSerialInterface::RoSoftS88XPressNetLI:
baudrate.setValueInternal(9600);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterface::LenzLI100F:
baudrate.setValueInternal(19200);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterface::LenzLI101F:
baudrate.setValueInternal(19200);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
}
updateEnabled();
updateVisible();
}},
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject},
s88StartAddress{this, "s88_start_address", XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressDefault, PropertyFlags::ReadWrite | PropertyFlags::Store},
s88ModuleCount{this, "s88_module_count", XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountDefault, PropertyFlags::ReadWrite | PropertyFlags::Store}
{
name = "XpressNet (serial)";
xpressnet.setValueInternal(std::make_shared<XpressNet::XpressNet>(*this, xpressnet.name(), std::bind(&XpressNetSerial::send, this, std::placeholders::_1)));
Attributes::addValues(interface, XpressNetSerialInterfaceValues);
Attributes::addEnabled(interface, !online);
m_interfaceItems.insertBefore(interface, baudrate);
Attributes::addDisplayName(xpressnet, DisplayName::Hardware::xpressnet);
m_interfaceItems.insertBefore(xpressnet, notes);
Attributes::addMinMax(s88StartAddress, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMin, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMax);
Attributes::addEnabled(s88StartAddress, !online);
Attributes::addVisible(s88StartAddress, false);
m_interfaceItems.insertBefore(s88StartAddress, notes);
Attributes::addMinMax(s88ModuleCount, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMin, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMax);
Attributes::addEnabled(s88ModuleCount, !online);
Attributes::addVisible(s88ModuleCount, false);
m_interfaceItems.insertBefore(s88ModuleCount, notes);
updateEnabled();
updateVisible();
}
void XpressNetSerial::loaded()
{
SerialCommandStation::loaded();
updateEnabled();
updateVisible();
}
void XpressNetSerial::worldEvent(WorldState state, WorldEvent event)
{
SerialCommandStation::worldEvent(state, event);
if(event == WorldEvent::EditEnabled || event == WorldEvent::EditDisabled)
updateEnabled();
}
void XpressNetSerial::emergencyStopChanged(bool value)
{
CommandStation::emergencyStopChanged(value);
if(online)
xpressnet->emergencyStopChanged(value);
}
void XpressNetSerial::powerOnChanged(bool value)
{
CommandStation::powerOnChanged(value);
if(online)
xpressnet->powerOnChanged(value);
}
void XpressNetSerial::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
CommandStation::decoderChanged(decoder, changes, functionNumber);
if(online)
xpressnet->decoderChanged(decoder, changes, functionNumber);
}
bool XpressNetSerial::send(const XpressNet::Message& msg)
{
assert(XpressNet::isChecksumValid(msg));
if(!m_serialPort.is_open())
return false;
boost::system::error_code ec;
m_serialPort.write_some(boost::asio::buffer(static_cast<const void*>(&msg), msg.size()), ec); // TODO async
if(ec)
{
Log::log(*this, LogMessage::E2001_SERIAL_WRITE_FAILED_X, ec);
return false;
}
return true;
}
void XpressNetSerial::stop()
{
SerialCommandStation::stop();
updateEnabled();
}
void XpressNetSerial::started()
{
SerialCommandStation::started();
updateEnabled();
switch(interface.value())
{
case XpressNetSerialInterface::RoSoftS88XPressNetLI:
{
send(XpressNet::RoSoftS88XpressNetLI::S88StartAddress(s88StartAddress));
send(XpressNet::RoSoftS88XpressNetLI::S88ModuleCount(s88ModuleCount));
break;
}
default:
break;
}
}
void XpressNetSerial::read()
{
m_serialPort.async_read_some(boost::asio::buffer(m_readBuffer.data() + m_readBufferOffset, m_readBuffer.size() - m_readBufferOffset),
[this](const boost::system::error_code& ec, std::size_t bytesTransferred)
{
if(!ec)
{
const uint8_t* pos = m_readBuffer.data();
bytesTransferred += m_readBufferOffset;
while(bytesTransferred > 1)
{
const XpressNet::Message* message = reinterpret_cast<const XpressNet::Message*>(pos);
size_t drop = 0;
while(message->size() <= bytesTransferred && !XpressNet::isChecksumValid(*message) && drop < bytesTransferred)
{
drop++;
pos++;
bytesTransferred--;
message = reinterpret_cast<const XpressNet::Message*>(pos);
}
if(drop != 0)
{
EventLoop::call(
[this, drop]()
{
Log::log(*this, LogMessage::W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES, drop);
});
}
if(message->size() <= bytesTransferred)
{
bool handled = false;
if(message->header == 0xF2)
{
switch(*(pos + 1))
{
case 0xF1:
{
using XpressNet::RoSoftS88XpressNetLI::S88StartAddress;
EventLoop::call(
[this, msg = *static_cast<const S88StartAddress*>(message)]()
{
assert(msg.startAddress >= S88StartAddress::startAddressMin && msg.startAddress <= S88StartAddress::startAddressMax);
s88StartAddress.setValueInternal(msg.startAddress);
});
handled = true;
break;
}
case 0xF2:
{
using XpressNet::RoSoftS88XpressNetLI::S88ModuleCount;
EventLoop::call(
[this, msg = *static_cast<const S88ModuleCount*>(message)]()
{
assert(msg.moduleCount >= S88ModuleCount::moduleCountMin && msg.moduleCount <= S88ModuleCount::moduleCountMax);
s88ModuleCount.setValueInternal(msg.moduleCount);
});
handled = true;
break;
}
}
}
if(!handled)
xpressnet->receive(*message);
pos += message->size();
bytesTransferred -= message->size();
}
else
break;
}
if(bytesTransferred != 0)
memmove(m_readBuffer.data(), pos, bytesTransferred);
m_readBufferOffset = bytesTransferred;
read();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2002_SERIAL_READ_FAILED_X, ec);
online = false;
});
});
}
void XpressNetSerial::updateEnabled()
{
auto w = m_world.lock();
const bool enabled = w && contains(w->state.value(), WorldState::Edit) && !online;
Attributes::setEnabled(interface, enabled);
Attributes::setEnabled(s88StartAddress, enabled);
Attributes::setEnabled(s88ModuleCount, enabled);
}
void XpressNetSerial::updateVisible()
{
const bool visible = interface == XpressNetSerialInterface::RoSoftS88XPressNetLI;
Attributes::setVisible(s88StartAddress, visible);
Attributes::setVisible(s88ModuleCount, visible);
}

Datei anzeigen

@ -1,61 +0,0 @@
/**
* server/src/hardware/commandstation/xpressnetserial.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_COMMANDSTATION_XPRESSNETSERIAL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_XPRESSNETSERIAL_HPP
#include "serialcommandstation.hpp"
#include "../../enum/xpressnetserialinterface.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
class XpressNetSerial : public SerialCommandStation
{
private:
void updateEnabled();
void updateVisible();
protected:
void loaded() final;
void worldEvent(WorldState state, WorldEvent event) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
bool send(const XpressNet::Message& msg);
void stop() final;
void started() final;
void read() final;
public:
CLASS_ID("command_station.xpressnet_serial")
CREATE(XpressNetSerial)
Property<XpressNetSerialInterface> interface;
ObjectProperty<XpressNet::XpressNet> xpressnet;
Property<uint8_t> s88StartAddress;
Property<uint8_t> s88ModuleCount;
XpressNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,114 +0,0 @@
/**
* server/src/hardware/controller/controller.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 "controller.hpp"
#include "controllerlist.hpp"
#include "controllerlisttablemodel.hpp"
#include "../commandstation/commandstation.hpp"
#include "../../world/getworld.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
Controller::Controller(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)
{
std::shared_ptr<Controller> controller = shared_ptr<Controller>();
assert(controller);
if(commandStation)
commandStation->controllers->removeObject(controller);
if(value)
value->controllers->addObject(controller);
return true;
}},
active{this, "active", false, PropertyFlags::ReadWrite | PropertyFlags::StoreState, nullptr,
std::bind(&Controller::setActive, this, std::placeholders::_1)},
notes{this, "notes", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
{
auto world = _world.lock();
const bool editable = world && contains(world->state.value(), WorldState::Edit);
Attributes::addDisplayName(name, DisplayName::Object::name);
m_interfaceItems.add(name);
Attributes::addDisplayName(commandStation, DisplayName::Hardware::commandStation);
Attributes::addEnabled(commandStation, editable);
Attributes::addObjectList(commandStation, world->commandStations);
m_interfaceItems.add(commandStation);
Attributes::addDisplayName(active, DisplayName::Controller::active);
m_interfaceItems.add(active);
Attributes::addDisplayName(notes, DisplayName::Object::notes);
m_interfaceItems.add(notes);
}
void Controller::addToWorld()
{
IdObject::addToWorld();
if(auto world = m_world.lock())
world->controllers->addObject(shared_ptr<Controller>());
}
void Controller::destroying()
{
if(commandStation.value())
commandStation = nullptr;
if(auto world = m_world.lock())
world->controllers->removeObject(shared_ptr<Controller>());
IdObject::destroying();
}
void Controller::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(commandStation, editable);
try
{
switch(event)
{
case WorldEvent::Offline:
active = false;
break;
case WorldEvent::Online:
active = true;
break;
default:
break;
}
}
catch(...)
{
}
}

Datei anzeigen

@ -1,59 +0,0 @@
/**
* server/src/hardware/controller/controller.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_CONTROLLER_CONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLER_HPP
#include "../../core/idobject.hpp"
#include "../../core/property.hpp"
#include "../../core/objectproperty.hpp"
#include "../../core/commandstationproperty.hpp"
class Decoder;
enum class DecoderChangeFlags;
class Controller : public IdObject
{
friend class CommandStation;
protected:
void addToWorld() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) override;
virtual bool setActive(bool& value) = 0;
virtual void emergencyStopChanged(bool value) = 0;
virtual void powerOnChanged(bool value) = 0;
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) = 0;
public:
Property<std::string> name;
CommandStationProperty commandStation;
Property<bool> active;
Property<std::string> notes;
Controller(const std::weak_ptr<World>& _world, std::string_view _id);
};
#endif

Datei anzeigen

@ -1,85 +0,0 @@
/**
* server/src/hardware/controller/controllerlist.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 "controllerlist.hpp"
#include "controllerlisttablemodel.hpp"
#include "controllers.hpp"
#include "../commandstation/commandstation.hpp"
#include "../../world/getworld.hpp"
#include "../../core/attributes.hpp"
#include "../../utils/displayname.hpp"
ControllerList::ControllerList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<Controller>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view controllerClassId)
{
auto world = getWorld(&this->parent());
if(!world)
return std::shared_ptr<Controller>();
auto controller = Controllers::create(world, controllerClassId, world->getUniqueId("controller"));
if(auto* cs = dynamic_cast<CommandStation*>(&this->parent()))
controller->commandStation = cs->shared_ptr<CommandStation>();
//else if(world->commandStations->length() == 1)
// decoder->commandStation = cs->shared_ptr<CommandStation>();
return controller;
}}
, remove{*this, "remove",
[this](const std::shared_ptr<Controller>& controller)
{
if(containsObject(controller))
controller->destroy();
assert(!containsObject(controller));
}}
{
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, Controllers::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
Attributes::addEnabled(remove, editable);
m_interfaceItems.add(remove);
}
TableModelPtr ControllerList::getModel()
{
return std::make_shared<ControllerListTableModel>(*this);
}
void ControllerList::worldEvent(WorldState state, WorldEvent event)
{
ObjectList<Controller>::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(add, editable);
Attributes::setEnabled(remove, editable);
}
bool ControllerList::isListedProperty(const std::string& name)
{
return ControllerListTableModel::isListedProperty(name);
}

Datei anzeigen

@ -1,96 +0,0 @@
/**
* server/src/hardware/controller/usbxpressnetcontroller.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra <reinderfeenstra@gmail.com>
*
* 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 "usbxpressnetcontroller.hpp"
#include "../../core/traintastic.hpp"
#include "../../world/world.hpp"
#include "../../core/attributes.hpp"
USBXpressNetController::USBXpressNetController(const std::weak_ptr<World>& world, std::string_view _id) :
Controller(world, _id),
m_handle{nullptr},
serial{this, "serial", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
mode{this, "mode", USBXpressNetControllerMode::Direct, PropertyFlags::ReadWrite | PropertyFlags::Store}
{
name = "USB XpressNet controller";
m_interfaceItems.insertBefore(serial, notes);
Attributes::addValues(mode, USBXpressNetControllerModeValues);
m_interfaceItems.insertBefore(mode, notes);
usbxpressnet_init();
}
USBXpressNetController::~USBXpressNetController()
{
if(m_handle)
{
usbxpressnet_reset(m_handle);
usbxpressnet_close(m_handle);
}
usbxpressnet_fini();
}
bool USBXpressNetController::setActive(bool& value)
{
if(!m_handle && value)
{
usbxpressnet_status status = usbxpressnet_open(!serial.value().empty() ? serial.value().c_str() : nullptr, &m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_open: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_reset(m_handle);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_reset: ") + usbxpressnet_status_str(status));
return false;
}
status = usbxpressnet_set_mode(m_handle, USBXPRESSNET_MODE_STATION, 0);
if(status != USBXPRESSNET_STATUS_SUCCESS)
{
// \todo Log::log(*this, LogMessage::E0001_X, std::string("usbxpressnet_set_mode: ") + usbxpressnet_status_str(status));
return false;
}
}
else if(m_handle && !value)
{
usbxpressnet_close(m_handle);
m_handle = nullptr;
}
return true;
}
void USBXpressNetController::emergencyStopChanged(bool value)
{
}
void USBXpressNetController::trackPowerChanged(bool value)
{
}
void USBXpressNetController::decoderChanged(const Decoder& decoder, DecoderChangeFlags, uint32_t)
{
}

Datei anzeigen

@ -1,52 +0,0 @@
/**
* server/src/hardware/controller/usbxpressnetcontroller.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_HARDWARE_CONTROLLER_USBXPRESSNETCONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_USBXPRESSNETCONTROLLER_HPP
#include "controller.hpp"
#include <usbxpressnet.h>
#include "../../enum/usbxpressnetcontrollermode.hpp"
class USBXpressNetController : public Controller
{
protected:
usbxpressnet_handle m_handle;
bool setActive(bool& value) final;
void emergencyStopChanged(bool value) final;
void trackPowerChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags, uint32_t) final;
public:
CLASS_ID("controller.usb_xpressnet_controller")
CREATE(USBXpressNetController)
Property<std::string> serial;
Property<USBXpressNetControllerMode> mode;
USBXpressNetController(const std::weak_ptr<World>& world, std::string_view _id);
~USBXpressNetController() final;
};
#endif

Datei anzeigen

@ -1,437 +0,0 @@
/**
* server/src/hardware/controller/wlanmaus.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 "wlanmaus.hpp"
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
#include "../commandstation/commandstation.hpp"
#include "../decoder/decoder.hpp"
#include "../protocol/z21/messages.hpp"
#include "../../utils/tohex.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
static std::string toString(const boost::asio::ip::udp::endpoint& endpoint)
{
std::stringstream ss;
ss << endpoint;
return ss.str();
}
WLANmaus::WLANmaus(const std::weak_ptr<World> world, std::string_view _id) :
Controller(world, _id),
m_socket{Traintastic::instance->ioContext()},
m_blockLocoInfo{nullptr},
m_debugLog{false},
port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store},
debugLog{this, "debug_log", m_debugLog, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](bool value)
{
m_debugLog = value;
}}
{
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, !active);
m_interfaceItems.add(port);
m_interfaceItems.add(debugLog);
}
bool WLANmaus::setActive(bool& value)
{
if(!m_socket.is_open() && value)
{
boost::system::error_code ec;
if(m_socket.open(boost::asio::ip::udp::v4(), ec))
{
Log::log(*this, LogMessage::E2004_SOCKET_OPEN_FAILED_X, ec);
return false;
}
else if(m_socket.bind(boost::asio::ip::udp::endpoint(boost::asio::ip::address_v4::any(), port), ec))
{
m_socket.close();
Log::log(*this, LogMessage::E2006_SOCKET_BIND_FAILED_X, ec);
return false;
}
receive(); // start receiving messages
// TODO: send message were alive ??
Attributes::setEnabled(port, false);
}
else if(m_socket.is_open() && !value)
{
Attributes::setEnabled(port, true);
m_socket.close();
}
return true;
}
void WLANmaus::emergencyStopChanged(bool value)
{
if(value)
{
for(auto it : m_clients)
if(it.second.broadcastFlags & Z21::PowerLocoTurnout)
sendTo(Z21::LanXBCStopped(), it.first);
}
else if(commandStation && commandStation->powerOn) // send z21_lan_x_bc_track_power_on if power is on
{
for(auto it : m_clients)
if(it.second.broadcastFlags & Z21::PowerLocoTurnout)
sendTo(Z21::LanXBCTrackPowerOn(), it.first);
}
}
void WLANmaus::powerOnChanged(bool value)
{
if(value)
{
for(auto it : m_clients)
if(it.second.broadcastFlags & Z21::PowerLocoTurnout)
sendTo(Z21::LanXBCTrackPowerOn(), it.first);
}
else
{
for(auto it : m_clients)
if(it.second.broadcastFlags & Z21::PowerLocoTurnout)
sendTo(Z21::LanXBCTrackPowerOff(), it.first);
}
}
void WLANmaus::decoderChanged(const Decoder& decoder, DecoderChangeFlags, uint32_t)
{
if(&decoder == m_blockLocoInfo)
return;
EventLoop::call(
[this, dec=decoder.shared_ptr_c<const Decoder>()]()
{
broadcastLocoInfo(*dec);
});
}
void WLANmaus::receive()
{
m_socket.async_receive_from(boost::asio::buffer(m_receiveBuffer), m_receiveEndpoint,
[this](const boost::system::error_code& ec, std::size_t bytesReceived)
{
if(!ec)
{
if((bytesReceived >= sizeof(Z21::Message)))
{
const Z21::Message* message = reinterpret_cast<const Z21::Message*>(m_receiveBuffer.data());
if(m_debugLog)
EventLoop::call(
[this, src=toString(m_receiveEndpoint), data=Z21::toString(*message)]()
{
Log::log(*this, LogMessage::D2005_X_RX_X, src, data);
});
switch(message->header())
{
case Z21::LAN_X:
{
// TODO check XOR
const uint8_t xheader = static_cast<const Z21::LanX*>(message)->xheader;
switch(xheader)
{
case 0x21:
if(*message == Z21::LanXGetStatus())
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
Z21::LanXStatusChanged response;
if(!commandStation || commandStation->emergencyStop)
response.db1 |= Z21_CENTRALSTATE_EMERGENCYSTOP;
if(!commandStation || !commandStation->powerOn)
response.db1 |= Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
response.calcChecksum();
sendTo(response, endpoint);
});
}
else if(*message == Z21::LanXSetTrackPowerOn())
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
if(commandStation)
{
commandStation->emergencyStop = false;
commandStation->powerOn = true;
if(commandStation->powerOn)
sendTo(Z21::LanXBCTrackPowerOn(), endpoint);
}
});
}
else if(*message == Z21::LanXSetTrackPowerOff())
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
if(commandStation)
{
commandStation->powerOn = false;
if(!commandStation->powerOn)
sendTo(Z21::LanXBCTrackPowerOff(), endpoint);
}
});
}
break;
case 0x80:
if(*message == Z21::LanXSetStop())
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
if(commandStation)
{
commandStation->emergencyStop = true;
if(commandStation->emergencyStop)
sendTo(Z21::LanXBCStopped(), endpoint);
}
});
}
break;
case 0xE3:
if(const Z21::LanXGetLocoInfo* r = static_cast<const Z21::LanXGetLocoInfo*>(message);
r->db0 == 0xF0)
{
EventLoop::call(
[this, request=*r, endpoint=m_receiveEndpoint]()
{
if(commandStation)
if(auto decoder = commandStation->getDecoder(DecoderProtocol::DCC, request.address(), request.isLongAddress()))
{
m_clients[endpoint].locoInfo.insert(locoInfoKey(request.address(), request.isLongAddress()));
sendTo(Z21::LanXLocoInfo(*decoder), endpoint);
}
});
}
break;
case 0xE4:
if(const Z21::LanXSetLocoDrive* locoDrive = static_cast<const Z21::LanXSetLocoDrive*>(message);
locoDrive->db0 >= 0x10 && locoDrive->db0 <= 0x13)
{
EventLoop::call(
[this, request=*locoDrive]()
{
if(!commandStation)
return;
if(auto decoder = commandStation->getDecoder(DecoderProtocol::DCC, request.address(), request.isLongAddress()))
{
//m_blockLocoInfo = decoder.get();
decoder->direction = request.direction();
decoder->emergencyStop = request.isEmergencyStop();
decoder->throttle = Decoder::speedStepToThrottle(request.speedStep(), request.speedSteps());
//broadcastLocoInfo(*decoder);
//m_blockLocoInfo = nullptr;
}
else
Log::log(*this, LogMessage::I2001_UNKNOWN_LOCO_ADDRESS_X, request.address());
});
}
else if(const Z21::LanXSetLocoFunction* locoFunction = static_cast<const Z21::LanXSetLocoFunction*>(message);
locoFunction->db0 == 0xF8 &&
locoFunction->switchType() != Z21::LanXSetLocoFunction::SwitchType::Invalid)
{
EventLoop::call(
[this, request=*locoFunction]()
{
if(commandStation)
if(auto decoder = commandStation->getDecoder(DecoderProtocol::DCC, request.address(), request.isLongAddress()))
if(auto function = decoder->getFunction(request.functionIndex()))
switch(request.switchType())
{
case Z21::LanXSetLocoFunction::SwitchType::Off:
function->value = false;
break;
case Z21::LanXSetLocoFunction::SwitchType::On:
function->value = true;
break;
case Z21::LanXSetLocoFunction::SwitchType::Toggle:
function->value = !function->value;
break;
case Z21::LanXSetLocoFunction::SwitchType::Invalid:
assert(false);
break;
}
});
}
break;
case 0xF1:
if(*message == Z21::LanXGetFirmwareVersion())
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
sendTo(Z21::LanXGetFirmwareVersionReply(1, 30), endpoint);
});
}
break;
}
break;
}
case Z21::LAN_GET_LOCO_MODE:
if(message->dataLen() == sizeof(Z21::LanGetLocoMode))
{
// TODO: reply without invoking event loop
EventLoop::call(
[this, address=static_cast<const Z21::LanGetLocoMode*>(message)->address(), endpoint=m_receiveEndpoint]()
{
sendTo(Z21::LanGetLocoModeReply(address, Z21::LocoMode::DCC), endpoint);
});
}
break;
case Z21::LAN_SET_LOCO_MODE:
if(message->dataLen() == sizeof(Z21::LanSetLocoMode))
{
// ignore, we always report DCC
}
break;
case Z21::LAN_GET_SERIAL_NUMBER:
if(message->dataLen() == sizeof(Z21::LanGetSerialNumber))
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
sendTo(Z21::LanGetSerialNumberReply(123456789), endpoint);
});
}
break;
case Z21::LAN_GET_HWINFO:
if(message->dataLen() == sizeof(Z21::LanGetHardwareInfo))
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
sendTo(Z21::LanGetHardwareInfoReply(Z21::HWT_Z21_START, 1, 30), endpoint);
});
}
break;
case Z21::LAN_SET_BROADCASTFLAGS:
if(message->dataLen() == sizeof(Z21::LanSetBroadcastFlags))
{
EventLoop::call(
[this, broadcastFlags=static_cast<const Z21::LanSetBroadcastFlags*>(message)->broadcastFlags(), endpoint=m_receiveEndpoint]()
{
m_clients[endpoint].broadcastFlags = broadcastFlags;
});
}
break;
case Z21::LAN_SYSTEMSTATE_GETDATA:
if(message->dataLen() == sizeof(Z21::LanSystemStateGetData))
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
Z21::LanSystemStateDataChanged response;
if(!commandStation || commandStation->emergencyStop)
response.centralState |= Z21_CENTRALSTATE_EMERGENCYSTOP;
if(!commandStation || !commandStation->powerOn)
response.centralState |= Z21_CENTRALSTATE_TRACKVOLTAGEOFF;
sendTo(response, endpoint);
});
}
break;
case Z21::LAN_LOGOFF:
if(message->dataLen() == sizeof(Z21::LanLogoff))
{
EventLoop::call(
[this, endpoint=m_receiveEndpoint]()
{
m_clients.erase(endpoint);
});
}
break;
default:
break;
}
}
receive();
}
else
EventLoop::call(
[this, ec]()
{
Log::log(*this, LogMessage::E2009_SOCKET_RECEIVE_FAILED_X, ec);
});
});
}
void WLANmaus::sendTo(const Z21::Message& message, const boost::asio::ip::udp::endpoint& endpoint)
{
if(debugLog)
Log::log(*this, LogMessage::D2004_X_TX_X, toString(endpoint), Z21::toString(message));
// TODO: add to queue, send async
boost::system::error_code ec;
m_socket.send_to(boost::asio::buffer(&message, message.dataLen()), endpoint, 0, ec);
if(ec)
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec); });
/*
m_socket.async_send_to(boost::asio::buffer(&msg, msg.dataLen), endpoint,
[this](const boost::system::error_code& ec, std::size_t)
{
if(ec)
EventLoop::call([this, ec](){ Log::log(*this, LogMessage::E2011_SOCKET_SEND_FAILED_X, ec); });
});
*/
}
void WLANmaus::broadcastLocoInfo(const Decoder& decoder)
{
const uint16_t key = locoInfoKey(decoder.address, decoder.longAddress);
const Z21::LanXLocoInfo message(decoder);
for(auto it : m_clients)
if(it.second.broadcastFlags & Z21::PowerLocoTurnout)
if(it.second.locoInfo.count(key))
sendTo(message, it.first);
}

Datei anzeigen

@ -1,82 +0,0 @@
/**
* server/src/hardware/controller/z21app.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_CONTROLLER_WLANMAUS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_WLANMAUS_HPP
#include "controller.hpp"
#include <map>
#include <set>
#include <boost/asio.hpp>
struct z21_lan_header;
namespace Z21 {
enum BroadcastFlags : uint32_t;
class Message;
}
class WLANmaus : public Controller
{
protected:
struct Client
{
Z21::BroadcastFlags broadcastFlags = static_cast<Z21::BroadcastFlags>(0);
std::set<uint16_t> locoInfo;
};
boost::asio::ip::udp::socket m_socket;
boost::asio::ip::udp::endpoint m_receiveEndpoint;
std::array<uint8_t,64> m_receiveBuffer;
std::map<boost::asio::ip::udp::endpoint, Client> m_clients;
Decoder* m_blockLocoInfo;
std::atomic_bool m_debugLog;
constexpr uint16_t locoInfoKey(uint16_t address, bool longAddress)
{
if(longAddress)
return address | 0xC000;
else
return address;
}
bool setActive(bool& value) final;
void emergencyStopChanged(bool value) final;
void powerOnChanged(bool value) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags, uint32_t) final;
void receive();
void sendTo(const Z21::Message& message, const boost::asio::ip::udp::endpoint& endpoint);
void broadcastLocoInfo(const Decoder& decoder);
public:
CLASS_ID("controller.wlanmaus")
CREATE(WLANmaus);
Property<uint16_t> port;
Property<bool> debugLog;
WLANmaus(const std::weak_ptr<World> world, std::string_view _id);
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -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,16 @@ 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);
decoderChanged(*this, changes, functionNumber);
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -26,11 +26,15 @@
#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"
#ifdef interface
#undef interface // interface is defined in combaseapi.h
#endif
enum class DecoderChangeFlags;
class DecoderFunction;
@ -43,6 +47,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 +79,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;
@ -85,6 +90,8 @@ class Decoder : public IdObject
ObjectProperty<DecoderFunctions> functions;
Property<std::string> notes;
boost::signals2::signal<void (Decoder&, DecoderChangeFlags, uint32_t)> decoderChanged;
Decoder(const std::weak_ptr<World>& world, std::string_view _id);
void addToWorld() final;

Datei anzeigen

@ -0,0 +1,92 @@
/**
* 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"
#include "decoderchangeflags.hpp"
#include "../../utils/almostzero.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;
});
}
}
void DecoderController::restoreDecoderSpeed()
{
for(const auto& decoder : m_decoders)
if(!decoder->emergencyStop && !almostZero(decoder->throttle.value()))
decoderChanged(*decoder, DecoderChangeFlags::Throttle, 0);
}

Datei anzeigen

@ -0,0 +1,57 @@
/**
* 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);
/// \brief restore speed of all decoders that are not (emergency) stopped
void restoreDecoderSpeed();
public:
[[nodiscard]] virtual bool addDecoder(Decoder& decoder);
[[nodiscard]] virtual bool removeDecoder(Decoder& decoder);
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress = false, bool fallbackToProtocolAuto = false);
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) = 0;
};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -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",
@ -69,6 +69,33 @@ TableModelPtr DecoderList::getModel()
return std::make_shared<DecoderListTableModel>(*this);
}
std::shared_ptr<Decoder> DecoderList::getDecoder(uint16_t address) const
{
auto it = std::find_if(begin(), end(),
[address](const auto& decoder)
{
return decoder->address.value() == address;
});
if(it != end())
return *it;
return {};
}
std::shared_ptr<Decoder> DecoderList::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const
{
auto it = std::find_if(begin(), end(),
[protocol, address, longAddress](const auto& decoder)
{
return
decoder->protocol.value() == protocol &&
decoder->address.value() == address &&
(protocol != DecoderProtocol::DCC || decoder->longAddress == longAddress);
});
if(it != end())
return *it;
return {};
}
void DecoderList::worldEvent(WorldState state, WorldEvent event)
{
ObjectList<Decoder>::worldEvent(state, event);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -42,6 +42,9 @@ class DecoderList : public ObjectList<Decoder>
DecoderList(Object& _parent, const std::string& parentPropertyName);
TableModelPtr getModel() final;
std::shared_ptr<Decoder> getDecoder(uint16_t address) const;
std::shared_ptr<Decoder> getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress = false) const;
};
#endif

Datei anzeigen

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

Datei anzeigen

@ -27,13 +27,23 @@
#include "../../core/objectproperty.hpp"
#include "../../core/objectvectorproperty.hpp"
#include "../../enum/tristate.hpp"
#include "inputcontroller.hpp"
#ifdef interface
#undef interface // interface is defined in combaseapi.h
#endif
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 +51,11 @@ class Input : public IdObject
void updateValue(TriState _value);
public:
static constexpr uint32_t invalidAddress = std::numeric_limits<uint32_t>::max();
Property<std::string> name;
ObjectProperty<InputController> interface;
Property<uint32_t> address;
Property<TriState> value;
ObjectVectorProperty<Object> consumers;

Datei anzeigen

@ -0,0 +1,103 @@
/**
* server/src/hardware/input/inputcontroller.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "inputcontroller.hpp"
#include "input.hpp"
#include "monitor/inputmonitor.hpp"
#include "../../utils/inrange.hpp"
bool InputController::isInputAddressAvailable(uint32_t address) const
{
return
inRange(address, inputAddressMinMax()) &&
m_inputs.find(address) == m_inputs.end();
}
uint32_t InputController::getUnusedInputAddress() const
{
const auto end = m_inputs.cend();
const auto range = inputAddressMinMax();
for(uint32_t address = range.first; address < range.second; address++)
if(m_inputs.find(address) == end)
return address;
return Input::invalidAddress;
}
bool InputController::changeInputAddress(Input& input, uint32_t newAddress)
{
assert(input.interface.value().get() == this);
if(!isInputAddressAvailable(newAddress))
return false;
auto node = m_inputs.extract(input.address); // old address
node.key() = newAddress;
m_inputs.insert(std::move(node));
input.value.setValueInternal(TriState::Undefined);
return true;
}
bool InputController::addInput(Input& input)
{
if(isInputAddressAvailable(input.address))
{
m_inputs.insert({input.address, input.shared_ptr<Input>()});
input.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
bool InputController::removeInput(Input& input)
{
assert(input.interface.value().get() == this);
auto it = m_inputs.find(input.address);
if(it != m_inputs.end() && it->second.get() == &input)
{
m_inputs.erase(it);
input.value.setValueInternal(TriState::Undefined);
return true;
}
else
return false;
}
void InputController::updateInputValue(uint32_t address, TriState value)
{
if(auto it = m_inputs.find(address); it != m_inputs.end())
it->second->updateValue(value);
if(auto monitor = m_inputMonitor.lock())
monitor->inputValueChanged(*monitor, address, value);
}
std::shared_ptr<InputMonitor> InputController::inputMonitor()
{
auto monitor = m_inputMonitor.lock();
if(!monitor)
{
monitor = std::make_shared<InputMonitor>(*this);
m_inputMonitor = monitor;
}
return monitor;
}

Datei anzeigen

@ -0,0 +1,101 @@
/**
* server/src/hardware/input/inputcontroller.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTCONTROLLER_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_INPUTCONTROLLER_HPP
#include <cstdint>
#include <unordered_map>
#include <memory>
#include "../../enum/tristate.hpp"
class Input;
class InputMonitor;
class InputController
{
public:
using InputMap = std::unordered_map<uint32_t, std::shared_ptr<Input>>;
protected:
InputMap m_inputs;
std::weak_ptr<InputMonitor> m_inputMonitor;
public:
/**
*
*/
inline const InputMap& inputs() const { return m_inputs; }
/**
*
*/
virtual std::pair<uint32_t, uint32_t> inputAddressMinMax() const = 0;
/**
*
*/
[[nodiscard]] virtual bool isInputAddressAvailable(uint32_t address) const;
/**
* @brief Get the next unused input address
*
* @return An usused address or #Input::invalidAddress if no unused address is available.
*/
uint32_t getUnusedInputAddress() const;
/**
*
* @return \c true if changed, \c false otherwise.
*/
[[nodiscard]] virtual bool changeInputAddress(Input& input, uint32_t newAddress);
/**
*
* @return \c true if added, \c false otherwise.
*/
[[nodiscard]] virtual bool addInput(Input& input);
/**
*
* @return \c true if removed, \c false otherwise.
*/
[[nodiscard]] virtual bool removeInput(Input& input);
/**
* @brief Update the input value
*
* This function should be called by the hardware layer whenever the input value changes.
*
* @param[in] address Input address
* @param[in] value New input value
*/
void updateInputValue(uint32_t address, TriState value);
/**
*
*
*/
std::shared_ptr<InputMonitor> inputMonitor();
};
#endif

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,104 +0,0 @@
/**
* server/src/hardware/input/loconetinput.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "loconetinput.hpp"
#include "../../core/attributes.hpp"
#include "../../world/world.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
LocoNetInput::LocoNetInput(const std::weak_ptr<World> world, std::string_view _id) :
Input(world, _id),
loconet{this, "loconet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<LocoNet::LocoNet>& newValue)
{
if(!newValue || newValue->addInput(*this))
{
if(loconet.value())
loconet->removeInput(*this);
return true;
}
return false;
}},
address{this, "address", addressMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint16_t& newValue)
{
if(loconet)
return loconet->changeInputAddress(*this, newValue);
return true;
}}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
Attributes::addEnabled(loconet, editable);
Attributes::addObjectList(loconet, w->loconets);
m_interfaceItems.add(loconet);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMin, addressMax);
m_interfaceItems.add(address);
}
void LocoNetInput::loaded()
{
Input::loaded();
if(loconet)
{
if(!loconet->addInput(*this))
{
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *loconet);
loconet.setValueInternal(nullptr);
}
}
}
void LocoNetInput::destroying()
{
if(loconet)
loconet = nullptr;
Input::destroying();
}
void LocoNetInput::worldEvent(WorldState state, WorldEvent event)
{
Input::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(loconet, editable);
Attributes::setEnabled(address, editable);
}
void LocoNetInput::idChanged(const std::string& newId)
{
if(loconet)
loconet->inputMonitorIdChanged(address, newId);
}
void LocoNetInput::valueChanged(TriState _value)
{
if(loconet)
loconet->inputMonitorValueChanged(address, _value);
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -1,52 +0,0 @@
/**
* server/src/hardware/input/monitor/xpressnetinputmonitor.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "xpressnetinputmonitor.hpp"
#include "../../protocol/xpressnet/xpressnet.hpp"
#include "../xpressnetinput.hpp"
XpressNetInputMonitor::XpressNetInputMonitor(std::shared_ptr<XpressNet::XpressNet> xpressnet) :
InputMonitor(),
m_xpressnet{std::move(xpressnet)}
{
addressMin.setValueInternal(XpressNetInput::addressMin);
addressMax.setValueInternal(XpressNetInput::addressMax);
m_xpressnet->m_inputMonitors.emplace_back(this);
}
XpressNetInputMonitor::~XpressNetInputMonitor()
{
if(auto it = std::find(m_xpressnet->m_inputMonitors.begin(), m_xpressnet->m_inputMonitors.end(), this); it != m_xpressnet->m_inputMonitors.end())
m_xpressnet->m_inputMonitors.erase(it);
}
std::vector<InputMonitor::InputInfo> XpressNetInputMonitor::getInputInfo() const
{
std::vector<InputInfo> inputInfo;
for(auto it : m_xpressnet->m_inputs)
{
XpressNetInput& input = *(it.second);
InputInfo info(input.address, input.id, input.value);
inputInfo.push_back(info);
}
return inputInfo;
}

Datei anzeigen

@ -1,104 +0,0 @@
/**
* server/src/hardware/input/xpressnetinput.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "xpressnetinput.hpp"
#include "../../core/attributes.hpp"
#include "../../world/world.hpp"
#include "../../log/log.hpp"
#include "../../utils/displayname.hpp"
XpressNetInput::XpressNetInput(const std::weak_ptr<World> world, std::string_view _id) :
Input(world, _id),
xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<XpressNet::XpressNet>& newValue)
{
if(!newValue || newValue->addInput(*this))
{
if(xpressnet.value())
xpressnet->removeInput(*this);
return true;
}
return false;
}},
address{this, "address", addressMin, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
[this](const uint16_t& newValue)
{
if(xpressnet)
return xpressnet->changeInputAddress(*this, newValue);
return true;
}}
{
auto w = world.lock();
const bool editable = w && contains(w->state.value(), WorldState::Edit);
Attributes::addDisplayName(xpressnet, DisplayName::Hardware::xpressnet);
Attributes::addEnabled(xpressnet, editable);
Attributes::addObjectList(xpressnet, w->xpressnets);
m_interfaceItems.add(xpressnet);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, editable);
Attributes::addMinMax(address, addressMin, addressMax);
m_interfaceItems.add(address);
}
void XpressNetInput::loaded()
{
Input::loaded();
if(xpressnet)
{
if(!xpressnet->addInput(*this))
{
Log::log(*this, LogMessage::C2001_ADDRESS_ALREADY_USED_AT_X, *xpressnet);
xpressnet.setValueInternal(nullptr);
}
}
}
void XpressNetInput::destroying()
{
if(xpressnet)
xpressnet = nullptr;
Input::destroying();
}
void XpressNetInput::worldEvent(WorldState state, WorldEvent event)
{
Input::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
Attributes::setEnabled(xpressnet, editable);
Attributes::setEnabled(address, editable);
}
void XpressNetInput::idChanged(const std::string& newId)
{
if(xpressnet)
xpressnet->inputMonitorIdChanged(address, newId);
}
void XpressNetInput::valueChanged(TriState _value)
{
if(xpressnet)
xpressnet->inputMonitorValueChanged(address, _value);
}

Datei anzeigen

@ -1,57 +0,0 @@
/**
* server/src/hardware/input/xpressnetinput.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INPUT_XPRESSNETINPUT_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INPUT_XPRESSNETINPUT_HPP
#include "input.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/xpressnet/xpressnet.hpp"
class XpressNetInput : public Input
{
friend class XpressNet::XpressNet;
protected:
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void valueChanged(TriState _value) final;
inline void updateValue(TriState _value) { Input::updateValue(_value); }
public:
CLASS_ID("input.xpressnet")
CREATE(XpressNetInput)
static constexpr uint16_t addressInvalid = 0;
static constexpr uint16_t addressMin = 1;
static constexpr uint16_t addressMax = 2048;
ObjectProperty<XpressNet::XpressNet> xpressnet;
Property<uint16_t> address;
XpressNetInput(const std::weak_ptr<World> world, std::string_view _id);
};
#endif

Datei anzeigen

@ -0,0 +1,263 @@
/**
* server/src/hardware/interface/dccplusplusinterface.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 "dccplusplusinterface.hpp"
#include "../input/list/inputlisttablemodel.hpp"
#include "../output/list/outputlisttablemodel.hpp"
#include "../protocol/dccplusplus/messages.hpp"
#include "../protocol/dccplusplus/iohandler/serialiohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/inrange.hpp"
#include "../../utils/serialport.hpp"
#include "../../world/world.hpp"
DCCPlusPlusInterface::DCCPlusPlusInterface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, baudrate{this, "baudrate", 115200, PropertyFlags::ReadWrite | PropertyFlags::Store}
, flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store}
, dccplusplus{this, "dccplusplus", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
{
dccplusplus.setValueInternal(std::make_shared<DCCPlusPlus::Settings>(*this, dccplusplus.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
Attributes::addDisplayName(device, DisplayName::Serial::device);
Attributes::addEnabled(device, !online);
m_interfaceItems.insertBefore(device, notes);
Attributes::addDisplayName(baudrate, DisplayName::Serial::baudrate);
Attributes::addEnabled(baudrate, !online);
Attributes::addMinMax(baudrate, SerialPort::baudrateMin, SerialPort::baudrateMax);
Attributes::addValues(baudrate, SerialPort::baudrateValues);
m_interfaceItems.insertBefore(baudrate, notes);
Attributes::addDisplayName(flowControl, DisplayName::Serial::flowControl);
Attributes::addEnabled(flowControl, !online);
Attributes::addValues(flowControl, SerialFlowControlValues);
m_interfaceItems.insertBefore(flowControl, notes);
Attributes::addDisplayName(dccplusplus, DisplayName::Hardware::dccplusplus);
m_interfaceItems.insertBefore(dccplusplus, notes);
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
}
bool DCCPlusPlusInterface::addDecoder(Decoder& decoder)
{
const bool success = DecoderController::addDecoder(decoder);
if(success)
decoders->addObject(decoder.shared_ptr<Decoder>());
return success;
}
bool DCCPlusPlusInterface::removeDecoder(Decoder& decoder)
{
const bool success = DecoderController::removeDecoder(decoder);
if(success)
decoders->removeObject(decoder.shared_ptr<Decoder>());
return success;
}
void DCCPlusPlusInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool DCCPlusPlusInterface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
m_kernel = DCCPlusPlus::Kernel::create<DCCPlusPlus::SerialIOHandler>(dccplusplus->config(), device.value(), baudrate.value(), flowControl.value());
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
if(auto w = m_world.lock())
{
const bool powerOn = contains(w->state.value(), WorldState::PowerOn);
if(!powerOn)
m_kernel->powerOff();
if(contains(w->state.value(), WorldState::Run))
{
m_kernel->clearEmergencyStop();
restoreDecoderSpeed();
}
else
m_kernel->emergencyStop();
if(powerOn)
m_kernel->powerOn();
}
});
m_kernel->setOnPowerOnChanged(
[this](bool powerOn)
{
if(auto w = m_world.lock())
{
if(powerOn && !contains(w->state.value(), WorldState::PowerOn))
w->powerOn();
else if(!powerOn && contains(w->state.value(), WorldState::PowerOn))
w->powerOff();
}
});
m_kernel->setDecoderController(this);
m_kernel->start();
m_dccplusplusPropertyChanged = dccplusplus->propertyChanged.connect(
[this](BaseProperty& property)
{
if(&property != &dccplusplus->startupDelay)
m_kernel->setConfig(dccplusplus->config());
});
Attributes::setEnabled({device, baudrate, flowControl}, false);
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
Attributes::setEnabled({device, baudrate, flowControl}, true);
m_dccplusplusPropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
}
return true;
}
void DCCPlusPlusInterface::addToWorld()
{
Interface::addToWorld();
if(auto world = m_world.lock())
{
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
}
}
void DCCPlusPlusInterface::loaded()
{
Interface::loaded();
check();
}
void DCCPlusPlusInterface::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
decoder->interface = nullptr;
}
if(auto world = m_world.lock())
{
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
}
Interface::destroying();
}
void DCCPlusPlusInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
m_kernel->powerOff();
break;
case WorldEvent::PowerOn:
m_kernel->powerOn();
break;
case WorldEvent::Stop:
m_kernel->emergencyStop();
break;
case WorldEvent::Run:
m_kernel->clearEmergencyStop();
restoreDecoderSpeed();
break;
default:
break;
}
}
}
void DCCPlusPlusInterface::check() const
{
for(const auto& decoder : *decoders)
checkDecoder(*decoder);
}
void DCCPlusPlusInterface::checkDecoder(const Decoder& decoder) const
{
if(decoder.protocol != DecoderProtocol::Auto && decoder.protocol != DecoderProtocol::DCC)
Log::log(decoder, LogMessage::C2002_DCCPLUSPLUS_ONLY_SUPPORTS_THE_DCC_PROTOCOL);
if(decoder.protocol == DecoderProtocol::DCC && decoder.address <= 127 && decoder.longAddress)
Log::log(decoder, LogMessage::C2003_DCCPLUSPLUS_DOESNT_SUPPORT_DCC_LONG_ADDRESSES_BELOW_128);
if(decoder.speedSteps != Decoder::speedStepsAuto && decoder.speedSteps != dccplusplus->speedSteps)
Log::log(decoder, LogMessage::W2003_COMMAND_STATION_DOESNT_SUPPORT_X_SPEEDSTEPS_USING_X, decoder.speedSteps.value(), dccplusplus->speedSteps.value());
for(const auto& function : *decoder.functions)
if(function->number > DCCPlusPlus::Config::functionNumberMax)
{
Log::log(decoder, LogMessage::W2002_COMMAND_STATION_DOESNT_SUPPORT_FUNCTIONS_ABOVE_FX, DCCPlusPlus::Config::functionNumberMax);
break;
}
}
void DCCPlusPlusInterface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}

Datei anzeigen

@ -0,0 +1,77 @@
/**
* server/src/hardware/interface/dccplusplusinterface.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_DCCPLUSPLUSINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_DCCPLUSPLUSINTERFACE_HPP
#include "interface.hpp"
#include "../protocol/dccplusplus/kernel.hpp"
#include "../protocol/dccplusplus/settings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/serialflowcontrol.hpp"
/**
* @brief DCC++(EX) hardware interface
*/
class DCCPlusPlusInterface final
: public Interface
, public DecoderController
{
CLASS_ID("interface.dccplusplus")
CREATE(DCCPlusPlusInterface)
DEFAULT_ID("dccplusplus")
private:
std::unique_ptr<DCCPlusPlus::Kernel> m_kernel;
boost::signals2::connection m_dccplusplusPropertyChanged;
void addToWorld() final;
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void check() const;
void checkDecoder(const Decoder& decoder) const;
void idChanged(const std::string& newId) final;
protected:
bool setOnline(bool& value) final;
public:
Property<std::string> device;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
ObjectProperty<DCCPlusPlus::Settings> dccplusplus;
ObjectProperty<DecoderList> decoders;
DCCPlusPlusInterface(const std::weak_ptr<World>& world, std::string_view _id);
// DecoderController:
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
};
#endif

Datei anzeigen

@ -0,0 +1,266 @@
/**
* server/src/hardware/interface/ecosinterface.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 "ecosinterface.hpp"
#include "../input/list/inputlisttablemodel.hpp"
#include "../output/list/outputlisttablemodel.hpp"
#include "../protocol/ecos/iohandler/tcpiohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/inrange.hpp"
#include "../../world/world.hpp"
ECoSInterface::ECoSInterface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, hostname{this, "hostname", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, ecos{this, "ecos", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, testCommand{this, "test_command", "", PropertyFlags::ReadWrite | PropertyFlags::NoStore}
, testCommandSend{*this, "test_command_send",
[this]()
{
if(m_kernel)
m_kernel->postSend(testCommand.value() + "\n");
}}
{
name = "ECoS";
ecos.setValueInternal(std::make_shared<ECoS::Settings>(*this, ecos.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
inputs.setValueInternal(std::make_shared<InputList>(*this, inputs.name()));
outputs.setValueInternal(std::make_shared<OutputList>(*this, outputs.name()));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
m_interfaceItems.insertBefore(hostname, notes);
m_interfaceItems.insertBefore(ecos, notes);
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
Attributes::addDisplayName(inputs, DisplayName::Hardware::inputs);
m_interfaceItems.insertBefore(inputs, notes);
Attributes::addDisplayName(outputs, DisplayName::Hardware::outputs);
m_interfaceItems.insertBefore(outputs, notes);
m_interfaceItems.add(testCommand);
m_interfaceItems.add(testCommandSend);
}
bool ECoSInterface::addDecoder(Decoder& decoder)
{
const bool success = DecoderController::addDecoder(decoder);
if(success)
decoders->addObject(decoder.shared_ptr<Decoder>());
return success;
}
bool ECoSInterface::removeDecoder(Decoder& decoder)
{
const bool success = DecoderController::removeDecoder(decoder);
if(success)
decoders->removeObject(decoder.shared_ptr<Decoder>());
return success;
}
void ECoSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool ECoSInterface::addInput(Input& input)
{
const bool success = InputController::addInput(input);
if(success)
inputs->addObject(input.shared_ptr<Input>());
return success;
}
bool ECoSInterface::removeInput(Input& input)
{
const bool success = InputController::removeInput(input);
if(success)
inputs->removeObject(input.shared_ptr<Input>());
return success;
}
bool ECoSInterface::addOutput(Output& output)
{
const bool success = OutputController::addOutput(output);
if(success)
outputs->addObject(output.shared_ptr<Output>());
return success;
}
bool ECoSInterface::removeOutput(Output& output)
{
const bool success = OutputController::removeOutput(output);
if(success)
outputs->removeObject(output.shared_ptr<Output>());
return success;
}
bool ECoSInterface::setOutputValue(uint32_t address, bool value)
{
return
m_kernel &&
inRange(address, outputAddressMinMax()) &&
m_kernel->setOutput(static_cast<uint16_t>(address), value);
}
bool ECoSInterface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
m_kernel = ECoS::Kernel::create<ECoS::TCPIOHandler>(ecos->config(), hostname.value());
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
});
m_kernel->setDecoderController(this);
m_kernel->setInputController(this);
m_kernel->setOutputController(this);
m_kernel->start();
m_ecosPropertyChanged = ecos->propertyChanged.connect(
[this](BaseProperty&)
{
m_kernel->setConfig(ecos->config());
});
if(auto w = m_world.lock())
{
if(contains(w->state.value(), WorldState::Run))
m_kernel->go();
else
m_kernel->emergencyStop();
}
Attributes::setEnabled(hostname, false);
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
Attributes::setEnabled(hostname, true);
m_ecosPropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
}
return true;
}
void ECoSInterface::addToWorld()
{
Interface::addToWorld();
if(auto world = m_world.lock())
{
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->add(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->add(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
}
void ECoSInterface::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
decoder->interface = nullptr;
}
for(const auto& input : *inputs)
{
assert(input->interface.value() == std::dynamic_pointer_cast<InputController>(shared_from_this()));
input->interface = nullptr;
}
for(const auto& output : *outputs)
{
assert(output->interface.value() == std::dynamic_pointer_cast<OutputController>(shared_from_this()));
output->interface = nullptr;
}
if(auto world = m_world.lock())
{
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->remove(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->remove(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
Interface::destroying();
}
void ECoSInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
case WorldEvent::Stop:
m_kernel->emergencyStop();
break;
case WorldEvent::PowerOn:
case WorldEvent::Run:
if(contains(state, WorldState::PowerOn | WorldState::Run))
m_kernel->go();
break;
default:
break;
}
}
}
void ECoSInterface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}

Datei anzeigen

@ -0,0 +1,93 @@
/**
* server/src/hardware/interface/ecosinterface.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_ECOSINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_ECOSINTERFACE_HPP
#include "interface.hpp"
#include "../protocol/ecos/kernel.hpp"
#include "../protocol/ecos/settings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../input/inputcontroller.hpp"
#include "../input/list/inputlist.hpp"
#include "../output/outputcontroller.hpp"
#include "../output/list/outputlist.hpp"
#include "../../core/objectproperty.hpp"
/**
* @brief ECoS hardware interface
*/
class ECoSInterface final
: public Interface
, public DecoderController
, public InputController
, public OutputController
{
CLASS_ID("interface.ecos")
DEFAULT_ID("ecos")
CREATE(ECoSInterface)
private:
std::unique_ptr<ECoS::Kernel> m_kernel;
boost::signals2::connection m_ecosPropertyChanged;
void addToWorld() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void typeChanged();
protected:
bool setOnline(bool& value) final;
public:
Property<std::string> hostname;
ObjectProperty<ECoS::Settings> ecos;
ObjectProperty<DecoderList> decoders;
ObjectProperty<InputList> inputs;
ObjectProperty<OutputList> outputs;
Property<std::string> testCommand;
Method<void()> testCommandSend;
ECoSInterface(const std::weak_ptr<World>& world, std::string_view _id);
// DecoderController:
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
std::pair<uint32_t, uint32_t> inputAddressMinMax() const final { return {1, 1}; }
[[nodiscard]] bool addInput(Input& input) final;
[[nodiscard]] bool removeInput(Input& input) final;
// OutputController:
std::pair<uint32_t, uint32_t> outputAddressMinMax() const final { return {1, 1}; }
[[nodiscard]] bool addOutput(Output& output) final;
[[nodiscard]] bool removeOutput(Output& output) final;
[[nodiscard]] bool setOutputValue(uint32_t address, bool value) final;
};
#endif

Datei anzeigen

@ -0,0 +1,88 @@
/**
* server/src/hardware/interface/interface.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022 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::Interface::online);
m_interfaceItems.add(online);
Attributes::addDisplayName(status, DisplayName::Interface::status);
Attributes::addValues(status, interfaceStatusValues);
m_interfaceItems.add(status);
Attributes::addDisplayName(notes, DisplayName::Object::notes);
m_interfaceItems.add(notes);
}
void Interface::addToWorld()
{
IdObject::addToWorld();
if(auto world = m_world.lock())
world->interfaces->addObject(shared_ptr<Interface>());
}
void Interface::destroying()
{
if(auto world = m_world.lock())
world->interfaces->removeObject(shared_ptr<Interface>());
IdObject::destroying();
}
void Interface::worldEvent(WorldState state, WorldEvent event)
{
IdObject::worldEvent(state, event);
Attributes::setEnabled(name, contains(state, WorldState::Edit));
switch(event)
{
case WorldEvent::Offline:
online = false;
break;
case WorldEvent::Online:
online = true;
break;
default:
break;
}
}

Datei anzeigen

@ -0,0 +1,50 @@
/**
* server/src/hardware/interface/interface.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACE_HPP
#include "../../core/idobject.hpp"
#include "../../enum/interfacestatus.hpp"
/**
* @brief Base class for a hardware interface
*/
class Interface : public IdObject
{
protected:
Interface(const std::weak_ptr<World>& world, std::string_view _id);
void addToWorld() override;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) override;
virtual bool setOnline(bool& value) = 0;
public:
Property<std::string> name;
Property<bool> online;
Property<InterfaceStatus> status;
Property<std::string> notes;
};
#endif

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/commandstation/commandstationlist.cpp
* server/src/hardware/interface/interfacelist.cpp
*
* 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,26 +20,26 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "commandstationlist.hpp"
#include "commandstationlisttablemodel.hpp"
#include "commandstations.hpp"
#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"
CommandStationList::CommandStationList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<CommandStation>(_parent, parentPropertyName),
InterfaceList::InterfaceList(Object& _parent, const std::string& parentPropertyName) :
ObjectList<Interface>(_parent, parentPropertyName),
add{*this, "add",
[this](std::string_view commandStationClassId)
[this](std::string_view interfaceClassId)
{
auto world = getWorld(this);
if(!world)
return std::shared_ptr<CommandStation>();
return CommandStations::create(world, commandStationClassId, world->getUniqueId("cs"));
return std::shared_ptr<Interface>();
return Interfaces::create(world, interfaceClassId);
}},
remove{*this, "remove",
[this](const std::shared_ptr<CommandStation>& object)
[this](const std::shared_ptr<Interface>& object)
{
if(containsObject(object))
object->destroy();
@ -51,7 +51,7 @@ CommandStationList::CommandStationList(Object& _parent, const std::string& paren
Attributes::addDisplayName(add, DisplayName::List::add);
Attributes::addEnabled(add, editable);
Attributes::addClassList(add, CommandStations::classList);
Attributes::addClassList(add, Interfaces::classList);
m_interfaceItems.add(add);
Attributes::addDisplayName(remove, DisplayName::List::remove);
@ -59,14 +59,14 @@ CommandStationList::CommandStationList(Object& _parent, const std::string& paren
m_interfaceItems.add(remove);
}
TableModelPtr CommandStationList::getModel()
TableModelPtr InterfaceList::getModel()
{
return std::make_shared<CommandStationListTableModel>(*this);
return std::make_shared<InterfaceListTableModel>(*this);
}
void CommandStationList::worldEvent(WorldState state, WorldEvent event)
void InterfaceList::worldEvent(WorldState state, WorldEvent event)
{
ObjectList<CommandStation>::worldEvent(state, event);
ObjectList<Interface>::worldEvent(state, event);
const bool editable = contains(state, WorldState::Edit);
@ -74,7 +74,7 @@ void CommandStationList::worldEvent(WorldState state, WorldEvent event)
Attributes::setEnabled(remove, editable);
}
bool CommandStationList::isListedProperty(const std::string& name)
bool InterfaceList::isListedProperty(const std::string& name)
{
return CommandStationListTableModel::isListedProperty(name);
return InterfaceListTableModel::isListedProperty(name);
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/controller/controllerlist.hpp
* server/src/hardware/interface/interfacelist.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,27 +20,26 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERLIST_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERLIST_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELIST_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELIST_HPP
#include "../../core/objectlist.hpp"
#include "../../core/method.hpp"
#include "controller.hpp"
#include "interface.hpp"
class ControllerList : public ObjectList<Controller>
class InterfaceList : public ObjectList<Interface>
{
protected:
void worldEvent(WorldState state, WorldEvent event) final;
bool isListedProperty(const std::string& name) final;
public:
CLASS_ID("controller_list")
CLASS_ID("list.interface")
Method<std::shared_ptr<Controller>(std::string_view)> add;
Method<void(const std::shared_ptr<Controller>&)> remove;
Method<std::shared_ptr<Interface>(std::string_view)> add;
Method<void(const std::shared_ptr<Interface>&)> remove;
ControllerList(Object& _parent, const std::string& parentPropertyName);
InterfaceList(Object& _parent, const std::string& parentPropertyName);
TableModelPtr getModel() final;
};

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/core/
* server/src/hardware/interface/interfacelisttablemodel.cpp
*
* This file is part of the traintastic source code
* 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,48 +20,48 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "controllerlisttablemodel.hpp"
#include "controllerlist.hpp"
#include "interfacelisttablemodel.hpp"
#include "interfacelist.hpp"
#include "../../utils/displayname.hpp"
constexpr uint32_t columnId = 0;
constexpr uint32_t columnName = 1;
constexpr uint32_t columnActive = 2;
constexpr uint32_t columnStatus = 2;
bool ControllerListTableModel::isListedProperty(const std::string& name)
bool InterfaceListTableModel::isListedProperty(const std::string& name)
{
return
name == "id" ||
name == "name" ||
name == "active";
name == "status";
}
ControllerListTableModel::ControllerListTableModel(ControllerList& list) :
ObjectListTableModel<Controller>(list)
InterfaceListTableModel::InterfaceListTableModel(InterfaceList& list) :
ObjectListTableModel<Interface>(list)
{
setColumnHeaders({
DisplayName::Object::id,
DisplayName::Object::name,
DisplayName::Controller::active,
DisplayName::Interface::status,
});
}
std::string ControllerListTableModel::getText(uint32_t column, uint32_t row) const
std::string InterfaceListTableModel::getText(uint32_t column, uint32_t row) const
{
if(row < rowCount())
{
const Controller& controller = getItem(row);
const Interface& interface = getItem(row);
switch(column)
{
case columnId:
return controller.id;
return interface.id;
case columnName:
return controller.name;
return interface.name;
case columnActive:
return controller.active ? "\u2022" : "";
case columnStatus:
return "<todo>";
default:
assert(false);
@ -72,12 +72,12 @@ std::string ControllerListTableModel::getText(uint32_t column, uint32_t row) con
return "";
}
void ControllerListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
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() == "active")
changed(row, columnActive);
else if(property.name() == "status")
changed(row, columnStatus);
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/commandstation/controllerlisttablemodel.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,27 +20,27 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERLISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERLISTTABLEMODEL_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELISTTABLEMODEL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACELISTTABLEMODEL_HPP
#include "../../core/objectlisttablemodel.hpp"
#include "controllerlist.hpp"
#include "interfacelist.hpp"
class ControllerList;
class InterfaceList;
class ControllerListTableModel : public ObjectListTableModel<Controller>
class InterfaceListTableModel : public ObjectListTableModel<Interface>
{
friend class ControllerList;
friend class InterfaceList;
protected:
void propertyChanged(BaseProperty& property, uint32_t row) final;
public:
CLASS_ID("controller_list_table_model")
CLASS_ID("table_model.list.interface")
static bool isListedProperty(const std::string& name);
ControllerListTableModel(ControllerList& list);
InterfaceListTableModel(InterfaceList& interfaceList);
std::string getText(uint32_t column, uint32_t row) const final;
};

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/controller/controllers.cpp
* server/src/hardware/interface/interfaces.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 Reinder Feenstra
* Copyright (C) 2021-2022 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,16 +20,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "controllers.hpp"
#include "interfaces.hpp"
#include "../../utils/ifclassidcreate.hpp"
#include "../../world/world.hpp"
std::shared_ptr<Controller> Controllers::create(const std::weak_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 == WLANmaus::classId)
return WLANmaus::create(world, id);
#ifdef USB_XPRESSNET
else if(classId == USBXpressNetController::classId)
return USBXpressNetController::create(world, id);
#endif
else
return std::shared_ptr<Controller>();
IF_CLASSID_CREATE(DCCPlusPlusInterface)
IF_CLASSID_CREATE(ECoSInterface)
IF_CLASSID_CREATE(LocoNetInterface)
IF_CLASSID_CREATE(WlanMausInterface)
IF_CLASSID_CREATE(XpressNetInterface)
IF_CLASSID_CREATE(Z21Interface)
return std::shared_ptr<Interface>();
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/controller/controllers.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-2022 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,33 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERS_HPP
#define TRAINTASTIC_SERVER_HARDWARE_CONTROLLER_CONTROLLERS_HPP
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACES_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_INTERFACES_HPP
#include "controller.hpp"
#include "interface.hpp"
#include "../../utils/makearray.hpp"
#include "wlanmaus.hpp"
#ifdef USB_XPRESSNET
#include "usbxpressnetcontroller.hpp"
#endif
#include "dccplusplusinterface.hpp"
#include "ecosinterface.hpp"
#include "loconetinterface.hpp"
#include "wlanmausinterface.hpp"
#include "xpressnetinterface.hpp"
#include "z21interface.hpp"
struct Controllers
struct Interfaces
{
static constexpr std::string_view classIdPrefix = "controller.";
static constexpr std::string_view classIdPrefix = "interface.";
static constexpr auto classList = makeArray(
#ifdef USB_XPRESSNET
USBXpressNetController::classId,
#endif
WLANmaus::classId
DCCPlusPlusInterface::classId,
ECoSInterface::classId,
LocoNetInterface::classId,
WlanMausInterface::classId,
XpressNetInterface::classId,
Z21Interface::classId
);
static std::shared_ptr<Controller> 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 = std::string_view{});
};
#endif
#endif

Datei anzeigen

@ -0,0 +1,374 @@
/**
* server/src/hardware/interface/loconetinterface.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 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 "loconetinterface.hpp"
#include "../input/list/inputlisttablemodel.hpp"
#include "../output/list/outputlisttablemodel.hpp"
#include "../protocol/loconet/iohandler/serialiohandler.hpp"
#include "../protocol/loconet/iohandler/tcpbinaryiohandler.hpp"
#include "../protocol/loconet/iohandler/lbserveriohandler.hpp"
#include "../protocol/loconet/iohandler/z21iohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/inrange.hpp"
#include "../../world/world.hpp"
LocoNetInterface::LocoNetInterface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, type{this, "type", LocoNetInterfaceType::Serial, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](LocoNetInterfaceType)
{
typeChanged();
}}
, device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store}
, flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store}
, hostname{this, "hostname", "192.168.1.203", PropertyFlags::ReadWrite | PropertyFlags::Store}
, port{this, "port", 5550, PropertyFlags::ReadWrite | PropertyFlags::Store}
, loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
{
name = "LocoNet";
loconet.setValueInternal(std::make_shared<LocoNet::Settings>(*this, loconet.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
inputs.setValueInternal(std::make_shared<InputList>(*this, inputs.name()));
outputs.setValueInternal(std::make_shared<OutputList>(*this, outputs.name()));
Attributes::addDisplayName(type, DisplayName::Interface::type);
Attributes::addEnabled(type, !online);
Attributes::addValues(type, locoNetInterfaceTypeValues);
m_interfaceItems.insertBefore(type, notes);
Attributes::addDisplayName(device, DisplayName::Serial::device);
Attributes::addEnabled(device, !online);
Attributes::addVisible(device, false);
m_interfaceItems.insertBefore(device, notes);
Attributes::addDisplayName(baudrate, DisplayName::Serial::baudrate);
Attributes::addEnabled(baudrate, !online);
Attributes::addVisible(baudrate, false);
m_interfaceItems.insertBefore(baudrate, notes);
Attributes::addDisplayName(flowControl, DisplayName::Serial::flowControl);
Attributes::addEnabled(flowControl, !online);
Attributes::addValues(flowControl, SerialFlowControlValues);
Attributes::addVisible(flowControl, false);
m_interfaceItems.insertBefore(flowControl, notes);
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
Attributes::addVisible(hostname, false);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, !online);
Attributes::addVisible(port, false);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(loconet, DisplayName::Hardware::loconet);
m_interfaceItems.insertBefore(loconet, notes);
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
Attributes::addDisplayName(inputs, DisplayName::Hardware::inputs);
m_interfaceItems.insertBefore(inputs, notes);
Attributes::addDisplayName(outputs, DisplayName::Hardware::outputs);
m_interfaceItems.insertBefore(outputs, notes);
typeChanged();
}
bool LocoNetInterface::addDecoder(Decoder& decoder)
{
const bool success = DecoderController::addDecoder(decoder);
if(success)
decoders->addObject(decoder.shared_ptr<Decoder>());
return success;
}
bool LocoNetInterface::removeDecoder(Decoder& decoder)
{
const bool success = DecoderController::removeDecoder(decoder);
if(success)
decoders->removeObject(decoder.shared_ptr<Decoder>());
return success;
}
void LocoNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool LocoNetInterface::addInput(Input& input)
{
const bool success = InputController::addInput(input);
if(success)
inputs->addObject(input.shared_ptr<Input>());
return success;
}
bool LocoNetInterface::removeInput(Input& input)
{
const bool success = InputController::removeInput(input);
if(success)
inputs->removeObject(input.shared_ptr<Input>());
return success;
}
bool LocoNetInterface::addOutput(Output& output)
{
const bool success = OutputController::addOutput(output);
if(success)
outputs->addObject(output.shared_ptr<Output>());
return success;
}
bool LocoNetInterface::removeOutput(Output& output)
{
const bool success = OutputController::removeOutput(output);
if(success)
outputs->removeObject(output.shared_ptr<Output>());
return success;
}
bool LocoNetInterface::setOutputValue(uint32_t address, bool value)
{
return
m_kernel &&
inRange(address, outputAddressMinMax()) &&
m_kernel->setOutput(static_cast<uint16_t>(address), value);
}
bool LocoNetInterface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
switch(type)
{
case LocoNetInterfaceType::Serial:
m_kernel = LocoNet::Kernel::create<LocoNet::SerialIOHandler>(loconet->config(), device.value(), baudrate.value(), flowControl.value());
break;
case LocoNetInterfaceType::TCPBinary:
m_kernel = LocoNet::Kernel::create<LocoNet::TCPBinaryIOHandler>(loconet->config(), hostname.value(), port.value());
break;
case LocoNetInterfaceType::LBServer:
m_kernel = LocoNet::Kernel::create<LocoNet::LBServerIOHandler>(loconet->config(), hostname.value(), port.value());
break;
case LocoNetInterfaceType::Z21:
m_kernel = LocoNet::Kernel::create<LocoNet::Z21IOHandler>(loconet->config(), hostname.value());
break;
default:
assert(false);
return false;
}
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
});
m_kernel->setOnGlobalPowerChanged(
[this](bool powerOn)
{
if(auto w = m_world.lock())
{
if(powerOn && !contains(w->state.value(), WorldState::PowerOn))
w->powerOn();
else if(!powerOn && contains(w->state.value(), WorldState::PowerOn))
w->powerOff();
}
});
m_kernel->setOnIdle(
[this]()
{
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
w->stop();
});
m_kernel->setDecoderController(this);
m_kernel->setInputController(this);
m_kernel->setOutputController(this);
m_kernel->start();
m_loconetPropertyChanged = loconet->propertyChanged.connect(
[this](BaseProperty&)
{
m_kernel->setConfig(loconet->config());
});
if(auto w = m_world.lock())
{
m_kernel->setPowerOn(contains(w->state.value(), WorldState::PowerOn));
if(contains(w->state.value(), WorldState::Run))
m_kernel->resume();
else
m_kernel->emergencyStop();
}
Attributes::setEnabled(type, false);
Attributes::setEnabled(device, false);
Attributes::setEnabled(baudrate, false);
Attributes::setEnabled(flowControl, false);
Attributes::setEnabled(hostname, false);
Attributes::setEnabled(port, false);
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
Attributes::setEnabled(type, true);
Attributes::setEnabled(device, true);
Attributes::setEnabled(baudrate, true);
Attributes::setEnabled(flowControl, true);
Attributes::setEnabled(hostname, true);
Attributes::setEnabled(port, true);
m_loconetPropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
}
return true;
}
void LocoNetInterface::addToWorld()
{
Interface::addToWorld();
if(auto world = m_world.lock())
{
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->add(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->add(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
}
void LocoNetInterface::loaded()
{
Interface::loaded();
typeChanged();
}
void LocoNetInterface::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
decoder->interface = nullptr;
}
for(const auto& input : *inputs)
{
assert(input->interface.value() == std::dynamic_pointer_cast<InputController>(shared_from_this()));
input->interface = nullptr;
}
for(const auto& output : *outputs)
{
assert(output->interface.value() == std::dynamic_pointer_cast<OutputController>(shared_from_this()));
output->interface = nullptr;
}
if(auto world = m_world.lock())
{
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->remove(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->remove(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
Interface::destroying();
}
void LocoNetInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
m_kernel->setPowerOn(false);
break;
case WorldEvent::PowerOn:
m_kernel->setPowerOn(true);
if(contains(state, WorldState::Run))
m_kernel->resume();
break;
case WorldEvent::Stop:
m_kernel->emergencyStop();
break;
case WorldEvent::Run:
if(contains(state, WorldState::PowerOn))
m_kernel->resume();
break;
default:
break;
}
}
}
void LocoNetInterface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}
void LocoNetInterface::typeChanged()
{
const bool serialVisible = isSerial(type);
Attributes::setVisible(device, serialVisible);
Attributes::setVisible(baudrate, serialVisible);
Attributes::setVisible(flowControl, serialVisible);
const bool networkVisible = isNetwork(type);
Attributes::setVisible(hostname, networkVisible);
Attributes::setVisible(port, networkVisible && type != LocoNetInterfaceType::Z21);
}

Datei anzeigen

@ -0,0 +1,99 @@
/**
* server/src/hardware/interface/loconetinterface.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_INTERFACE_LOCONETINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_LOCONETINTERFACE_HPP
#include "interface.hpp"
#include "../protocol/loconet/kernel.hpp"
#include "../protocol/loconet/settings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../input/inputcontroller.hpp"
#include "../input/list/inputlist.hpp"
#include "../output/outputcontroller.hpp"
#include "../output/list/outputlist.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/loconetinterfacetype.hpp"
#include "../../enum/serialflowcontrol.hpp"
/**
* @brief LocoNet hardware interface
*/
class LocoNetInterface final
: public Interface
, public DecoderController
, public InputController
, public OutputController
{
CLASS_ID("interface.loconet")
DEFAULT_ID("loconet")
CREATE(LocoNetInterface)
private:
std::unique_ptr<LocoNet::Kernel> m_kernel;
boost::signals2::connection m_loconetPropertyChanged;
void addToWorld() final;
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void typeChanged();
protected:
bool setOnline(bool& value) final;
public:
Property<LocoNetInterfaceType> type;
Property<std::string> device;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<LocoNet::Settings> loconet;
ObjectProperty<DecoderList> decoders;
ObjectProperty<InputList> inputs;
ObjectProperty<OutputList> outputs;
LocoNetInterface(const std::weak_ptr<World>& world, std::string_view _id);
// DecoderController:
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
std::pair<uint32_t, uint32_t> inputAddressMinMax() const final { return {LocoNet::Kernel::inputAddressMin, LocoNet::Kernel::inputAddressMax}; }
[[nodiscard]] bool addInput(Input& input) final;
[[nodiscard]] bool removeInput(Input& input) final;
// OutputController:
std::pair<uint32_t, uint32_t> outputAddressMinMax() const final { return {LocoNet::Kernel::outputAddressMin, LocoNet::Kernel::outputAddressMax}; }
[[nodiscard]] bool addOutput(Output& output) final;
[[nodiscard]] bool removeOutput(Output& output) final;
[[nodiscard]] bool setOutputValue(uint32_t address, bool value) final;
};
#endif

Datei anzeigen

@ -0,0 +1,137 @@
/**
* server/src/hardware/interface/wlanmausinterface.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 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 "wlanmausinterface.hpp"
#include "../protocol/z21/iohandler/udpserveriohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../world/world.hpp"
WlanMausInterface::WlanMausInterface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, z21{this, "z21", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
z21.setValueInternal(std::make_shared<Z21::ServerSettings>(*this, z21.name()));
Attributes::addDisplayName(z21, DisplayName::Hardware::z21);
m_interfaceItems.insertBefore(z21, notes);
}
void WlanMausInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
case WorldEvent::PowerOn:
case WorldEvent::Stop:
case WorldEvent::Run:
m_kernel->setState(contains(state, WorldState::PowerOn), !contains(state, WorldState::Run));
break;
default:
break;
}
}
}
void WlanMausInterface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}
bool WlanMausInterface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
auto world = m_world.lock();
assert(world);
m_kernel = Z21::ServerKernel::create<Z21::UDPServerIOHandler>(z21->config(), world->decoders.value());
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
});
m_kernel->setOnTrackPowerOff(
[this]()
{
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::PowerOn))
w->powerOff();
});
m_kernel->setOnTrackPowerOn(
[this]()
{
if(auto w = m_world.lock())
{
if(!contains(w->state.value(), WorldState::PowerOn))
w->powerOn();
if(!contains(w->state.value(), WorldState::Run))
w->run();
}
});
m_kernel->setOnEmergencyStop(
[this]()
{
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
w->stop();
});
m_kernel->start();
m_z21PropertyChanged = z21->propertyChanged.connect(
[this](BaseProperty&)
{
m_kernel->setConfig(z21->config());
});
if(auto w = m_world.lock())
m_kernel->setState(contains(w->state.value(), WorldState::PowerOn), !contains(w->state.value(), WorldState::Run));
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
}
return true;
}

Datei anzeigen

@ -1,9 +1,9 @@
/**
* server/src/hardware/input/loconetinput.hpp
* server/src/hardware/interface/wlanmausinterface.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 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,38 +20,37 @@
* 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
#ifndef TRAINTASTIC_SERVER_HARDWARE_INTERFACE_WLANMAUSINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_WLANMAUSINTERFACE_HPP
#include "input.hpp"
#include "interface.hpp"
#include "../protocol/z21/serverkernel.hpp"
#include "../protocol/z21/serversettings.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet/loconet.hpp"
class LocoNetInput : public Input
/**
* @brief WLANmaus/Z21 app hardware interface
*/
class WlanMausInterface : public Interface
{
friend class LocoNet::LocoNet;
CLASS_ID("interface.wlanmaus")
DEFAULT_ID("wlanmaus")
CREATE(WlanMausInterface)
private:
std::unique_ptr<Z21::ServerKernel> m_kernel;
boost::signals2::connection m_z21PropertyChanged;
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); }
bool setOnline(bool& value) final;
public:
CLASS_ID("input.loconet")
CREATE(LocoNetInput)
ObjectProperty<Z21::ServerSettings> z21;
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);
WlanMausInterface(const std::weak_ptr<World>& world, std::string_view _id);
};
#endif

Datei anzeigen

@ -0,0 +1,437 @@
/**
* server/src/hardware/interface/xpressnetinterface.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 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 "xpressnetinterface.hpp"
#include "../input/list/inputlisttablemodel.hpp"
#include "../output/list/outputlisttablemodel.hpp"
#include "../protocol/xpressnet/messages.hpp"
#include "../protocol/xpressnet/iohandler/serialiohandler.hpp"
#include "../protocol/xpressnet/iohandler/liusbiohandler.hpp"
#include "../protocol/xpressnet/iohandler/rosofts88xpressnetliiohandler.hpp"
#include "../protocol/xpressnet/iohandler/tcpiohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/inrange.hpp"
#include "../../world/world.hpp"
XpressNetInterface::XpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, type{this, "type", XpressNetInterfaceType::Serial, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](XpressNetInterfaceType)
{
updateVisible();
}}
, serialInterfaceType{this, "interface", XpressNetSerialInterfaceType::LenzLI100, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](XpressNetSerialInterfaceType value)
{
switch(value)
{
case XpressNetSerialInterfaceType::LenzLI100:
case XpressNetSerialInterfaceType::RoSoftS88XPressNetLI:
baudrate.setValueInternal(9600);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterfaceType::LenzLI100F:
case XpressNetSerialInterfaceType::LenzLI101F:
baudrate.setValueInternal(19200);
flowControl.setValueInternal(SerialFlowControl::Hardware);
break;
case XpressNetSerialInterfaceType::LenzLIUSB:
baudrate.setValueInternal(57600);
flowControl.setValueInternal(SerialFlowControl::None);
break;
case XpressNetSerialInterfaceType::DigikeijsDR5000:
baudrate.setValueInternal(115200);
flowControl.setValueInternal(SerialFlowControl::None);
break;
}
updateVisible();
}}
, device{this, "device", "", PropertyFlags::ReadWrite | PropertyFlags::Store}
, baudrate{this, "baudrate", 19200, PropertyFlags::ReadWrite | PropertyFlags::Store}
, flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store}
, hostname{this, "hostname", "192.168.1.203", PropertyFlags::ReadWrite | PropertyFlags::Store}
, port{this, "port", 5550, PropertyFlags::ReadWrite | PropertyFlags::Store}
, s88StartAddress{this, "s88_start_address", XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressDefault, PropertyFlags::ReadWrite | PropertyFlags::Store}
, s88ModuleCount{this, "s88_module_count", XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountDefault, PropertyFlags::ReadWrite | PropertyFlags::Store}
, xpressnet{this, "xpressnet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
{
xpressnet.setValueInternal(std::make_shared<XpressNet::Settings>(*this, xpressnet.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
inputs.setValueInternal(std::make_shared<InputList>(*this, inputs.name()));
outputs.setValueInternal(std::make_shared<OutputList>(*this, outputs.name()));
Attributes::addDisplayName(type, DisplayName::Interface::type);
Attributes::addEnabled(type, !online);
Attributes::addValues(type, xpressNetInterfaceTypeValues);
m_interfaceItems.insertBefore(type, notes);
Attributes::addValues(serialInterfaceType, XpressNetSerialInterfaceTypeValues);
Attributes::addEnabled(serialInterfaceType, !online);
Attributes::addVisible(serialInterfaceType, false);
m_interfaceItems.insertBefore(serialInterfaceType, notes);
Attributes::addDisplayName(device, DisplayName::Serial::device);
Attributes::addEnabled(device, !online);
Attributes::addVisible(device, false);
m_interfaceItems.insertBefore(device, notes);
Attributes::addDisplayName(baudrate, DisplayName::Serial::baudrate);
Attributes::addEnabled(baudrate, !online);
Attributes::addVisible(baudrate, false);
m_interfaceItems.insertBefore(baudrate, notes);
Attributes::addDisplayName(flowControl, DisplayName::Serial::flowControl);
Attributes::addEnabled(flowControl, !online);
Attributes::addValues(flowControl, SerialFlowControlValues);
Attributes::addVisible(flowControl, false);
m_interfaceItems.insertBefore(flowControl, notes);
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
Attributes::addVisible(hostname, false);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, !online);
Attributes::addVisible(port, false);
m_interfaceItems.insertBefore(port, notes);
Attributes::addMinMax(s88StartAddress, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMin, XpressNet::RoSoftS88XpressNetLI::S88StartAddress::startAddressMax);
Attributes::addEnabled(s88StartAddress, !online);
Attributes::addVisible(s88StartAddress, false);
m_interfaceItems.insertBefore(s88StartAddress, notes);
Attributes::addMinMax(s88ModuleCount, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMin, XpressNet::RoSoftS88XpressNetLI::S88ModuleCount::moduleCountMax);
Attributes::addEnabled(s88ModuleCount, !online);
Attributes::addVisible(s88ModuleCount, false);
m_interfaceItems.insertBefore(s88ModuleCount, notes);
Attributes::addDisplayName(xpressnet, DisplayName::Hardware::xpressnet);
m_interfaceItems.insertBefore(xpressnet, notes);
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
Attributes::addDisplayName(inputs, DisplayName::Hardware::inputs);
m_interfaceItems.insertBefore(inputs, notes);
Attributes::addDisplayName(outputs, DisplayName::Hardware::outputs);
m_interfaceItems.insertBefore(outputs, notes);
updateVisible();
}
bool XpressNetInterface::addDecoder(Decoder& decoder)
{
const bool success = DecoderController::addDecoder(decoder);
if(success)
decoders->addObject(decoder.shared_ptr<Decoder>());
return success;
}
bool XpressNetInterface::removeDecoder(Decoder& decoder)
{
const bool success = DecoderController::removeDecoder(decoder);
if(success)
decoders->removeObject(decoder.shared_ptr<Decoder>());
return success;
}
void XpressNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool XpressNetInterface::addInput(Input& input)
{
const bool success = InputController::addInput(input);
if(success)
inputs->addObject(input.shared_ptr<Input>());
return success;
}
bool XpressNetInterface::removeInput(Input& input)
{
const bool success = InputController::removeInput(input);
if(success)
inputs->removeObject(input.shared_ptr<Input>());
return success;
}
bool XpressNetInterface::addOutput(Output& output)
{
const bool success = OutputController::addOutput(output);
if(success)
outputs->addObject(output.shared_ptr<Output>());
return success;
}
bool XpressNetInterface::removeOutput(Output& output)
{
const bool success = OutputController::removeOutput(output);
if(success)
outputs->removeObject(output.shared_ptr<Output>());
return success;
}
bool XpressNetInterface::setOutputValue(uint32_t address, bool value)
{
return
m_kernel &&
inRange(address, outputAddressMinMax()) &&
m_kernel->setOutput(static_cast<uint16_t>(address), value);
}
bool XpressNetInterface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
switch(type)
{
case XpressNetInterfaceType::Serial:
switch(serialInterfaceType)
{
case XpressNetSerialInterfaceType::LenzLI100:
case XpressNetSerialInterfaceType::LenzLI100F:
case XpressNetSerialInterfaceType::LenzLI101F:
m_kernel = XpressNet::Kernel::create<XpressNet::SerialIOHandler>(xpressnet->config(), device.value(), baudrate.value(), flowControl.value());
break;
case XpressNetSerialInterfaceType::RoSoftS88XPressNetLI:
m_kernel = XpressNet::Kernel::create<XpressNet::RoSoftS88XPressNetLIIOHandler>(xpressnet->config(), device.value(), baudrate.value(), flowControl.value(), s88StartAddress.value(), s88ModuleCount.value());
break;
case XpressNetSerialInterfaceType::LenzLIUSB:
case XpressNetSerialInterfaceType::DigikeijsDR5000:
m_kernel = XpressNet::Kernel::create<XpressNet::LIUSBIOHandler>(xpressnet->config(), device.value(), baudrate.value(), flowControl.value());
break;
}
break;
case XpressNetInterfaceType::Network:
m_kernel = XpressNet::Kernel::create<XpressNet::TCPIOHandler>(xpressnet->config(), hostname.value(), port.value());
break;
}
if(!m_kernel)
{
assert(false);
return false;
}
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
});
m_kernel->setOnNormalOperationResumed(
[this]()
{
if(auto w = m_world.lock())
{
if(!contains(w->state.value(), WorldState::PowerOn))
w->powerOn();
if(!contains(w->state.value(), WorldState::Run))
w->run();
}
});
m_kernel->setOnTrackPowerOff(
[this]()
{
if(auto w = m_world.lock())
{
if(contains(w->state.value(), WorldState::PowerOn))
w->powerOff();
if(contains(w->state.value(), WorldState::Run))
w->stop();
}
});
m_kernel->setOnEmergencyStop(
[this]()
{
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
w->stop();
});
m_kernel->setDecoderController(this);
m_kernel->setInputController(this);
m_kernel->setOutputController(this);
m_kernel->start();
m_xpressnetPropertyChanged = xpressnet->propertyChanged.connect(
[this](BaseProperty&)
{
m_kernel->setConfig(xpressnet->config());
});
if(auto w = m_world.lock())
{
if(!contains(w->state.value(), WorldState::PowerOn))
m_kernel->trackPowerOff();
else if(!contains(w->state.value(), WorldState::Run))
m_kernel->emergencyStop();
else
m_kernel->normalOperationsResumed();
}
Attributes::setEnabled({type, serialInterfaceType, device, baudrate, flowControl, hostname, port, s88StartAddress, s88ModuleCount}, false);
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
Attributes::setEnabled({type, serialInterfaceType, device, baudrate, flowControl, hostname, port, s88StartAddress, s88ModuleCount}, true);
m_xpressnetPropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
}
return true;
}
void XpressNetInterface::addToWorld()
{
Interface::addToWorld();
if(auto world = m_world.lock())
{
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->add(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->add(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
}
void XpressNetInterface::loaded()
{
Interface::loaded();
updateVisible();
}
void XpressNetInterface::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
decoder->interface = nullptr;
}
for(const auto& input : *inputs)
{
assert(input->interface.value() == std::dynamic_pointer_cast<InputController>(shared_from_this()));
input->interface = nullptr;
}
for(const auto& output : *outputs)
{
assert(output->interface.value() == std::dynamic_pointer_cast<OutputController>(shared_from_this()));
output->interface = nullptr;
}
if(auto world = m_world.lock())
{
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
world->inputControllers->remove(std::dynamic_pointer_cast<InputController>(shared_from_this()));
world->outputControllers->remove(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
Interface::destroying();
}
void XpressNetInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
m_kernel->trackPowerOff();
break;
case WorldEvent::PowerOn:
m_kernel->normalOperationsResumed();
if(!contains(state, WorldState::Run))
m_kernel->emergencyStop();
break;
case WorldEvent::Stop:
m_kernel->emergencyStop();
break;
case WorldEvent::Run:
if(contains(state, WorldState::PowerOn))
m_kernel->normalOperationsResumed();
break;
default:
break;
}
}
}
void XpressNetInterface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}
void XpressNetInterface::updateVisible()
{
const bool isSerial = (type == XpressNetInterfaceType::Serial);
Attributes::setVisible(serialInterfaceType, isSerial);
Attributes::setVisible(device, isSerial);
Attributes::setVisible(baudrate, isSerial);
Attributes::setVisible(flowControl, isSerial);
const bool isNetwork = (type == XpressNetInterfaceType::Network);
Attributes::setVisible(hostname, isNetwork);
Attributes::setVisible(port, isNetwork);
const bool isRoSoftS88XPressNetLI = isSerial && (serialInterfaceType == XpressNetSerialInterfaceType::RoSoftS88XPressNetLI);
Attributes::setVisible(s88StartAddress, isRoSoftS88XPressNetLI);
Attributes::setVisible(s88ModuleCount, isRoSoftS88XPressNetLI);
}

Datei anzeigen

@ -0,0 +1,103 @@
/**
* server/src/hardware/interface/xpressnetinterface.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_INTERFACE_XPRESSNETINTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_XPRESSNETINTERFACE_HPP
#include "interface.hpp"
#include "../protocol/xpressnet/kernel.hpp"
#include "../protocol/xpressnet/settings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../input/inputcontroller.hpp"
#include "../input/list/inputlist.hpp"
#include "../output/outputcontroller.hpp"
#include "../output/list/outputlist.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/xpressnetinterfacetype.hpp"
#include "../../enum/xpressnetserialinterfacetype.hpp"
#include "../../enum/serialflowcontrol.hpp"
/**
* @brief XpressNet hardware interface
*/
class XpressNetInterface final
: public Interface
, public DecoderController
, public InputController
, public OutputController
{
CLASS_ID("interface.xpressnet")
DEFAULT_ID("xpressnet")
CREATE(XpressNetInterface)
private:
std::unique_ptr<XpressNet::Kernel> m_kernel;
boost::signals2::connection m_xpressnetPropertyChanged;
void addToWorld() final;
void loaded() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void updateVisible();
protected:
bool setOnline(bool& value) final;
public:
Property<XpressNetInterfaceType> type;
Property<XpressNetSerialInterfaceType> serialInterfaceType;
Property<std::string> device;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
Property<std::string> hostname;
Property<uint16_t> port;
Property<uint8_t> s88StartAddress;
Property<uint8_t> s88ModuleCount;
ObjectProperty<XpressNet::Settings> xpressnet;
ObjectProperty<DecoderList> decoders;
ObjectProperty<InputList> inputs;
ObjectProperty<OutputList> outputs;
XpressNetInterface(const std::weak_ptr<World>& world, std::string_view _id);
// DecoderController:
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:
std::pair<uint32_t, uint32_t> inputAddressMinMax() const final { return {XpressNet::Kernel::ioAddressMin, XpressNet::Kernel::ioAddressMax}; }
[[nodiscard]] bool addInput(Input& input) final;
[[nodiscard]] bool removeInput(Input& input) final;
// OutputController:
std::pair<uint32_t, uint32_t> outputAddressMinMax() const final { return {XpressNet::Kernel::ioAddressMin, XpressNet::Kernel::ioAddressMax}; }
[[nodiscard]] bool addOutput(Output& output) final;
[[nodiscard]] bool removeOutput(Output& output) final;
[[nodiscard]] bool setOutputValue(uint32_t address, bool value) final;
};
#endif

Datei anzeigen

@ -0,0 +1,260 @@
/**
* server/src/hardware/interface/z21interface.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 "z21interface.hpp"
#include "../decoder/decoderlisttablemodel.hpp"
#include "../protocol/z21/messages.hpp"
#include "../protocol/z21/iohandler/udpclientiohandler.hpp"
#include "../../core/attributes.hpp"
#include "../../log/log.hpp"
#include "../../log/logmessageexception.hpp"
#include "../../utils/category.hpp"
#include "../../utils/displayname.hpp"
#include "../../utils/inrange.hpp"
#include "../../world/world.hpp"
Z21Interface::Z21Interface(const std::weak_ptr<World>& world, std::string_view _id)
: Interface(world, _id)
, hostname{this, "hostname", "192.168.1.203", PropertyFlags::ReadWrite | PropertyFlags::Store}
, port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store}
, z21{this, "z21", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, hardwareType{this, "hardware_type", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, serialNumber{this, "serial_number", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, firmwareVersion{this, "firmware_version", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
z21.setValueInternal(std::make_shared<Z21::ClientSettings>(*this, z21.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
m_interfaceItems.insertBefore(hostname, notes);
Attributes::addDisplayName(port, DisplayName::IP::port);
Attributes::addEnabled(port, !online);
m_interfaceItems.insertBefore(port, notes);
Attributes::addDisplayName(z21, DisplayName::Hardware::z21);
m_interfaceItems.insertBefore(z21, notes);
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
Attributes::addCategory(hardwareType, Category::info);
m_interfaceItems.insertBefore(hardwareType, notes);
Attributes::addCategory(serialNumber, Category::info);
m_interfaceItems.insertBefore(serialNumber, notes);
Attributes::addCategory(firmwareVersion, Category::info);
m_interfaceItems.insertBefore(firmwareVersion, notes);
}
bool Z21Interface::addDecoder(Decoder& decoder)
{
const bool success = DecoderController::addDecoder(decoder);
if(success)
decoders->addObject(decoder.shared_ptr<Decoder>());
return success;
}
bool Z21Interface::removeDecoder(Decoder& decoder)
{
const bool success = DecoderController::removeDecoder(decoder);
if(success)
decoders->removeObject(decoder.shared_ptr<Decoder>());
return success;
}
void Z21Interface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool Z21Interface::setOnline(bool& value)
{
if(!m_kernel && value)
{
try
{
m_kernel = Z21::ClientKernel::create<Z21::UDPClientIOHandler>(z21->config(), hostname.value(), port.value());
status.setValueInternal(InterfaceStatus::Initializing);
m_kernel->setLogId(id.value());
m_kernel->setOnStarted(
[this]()
{
status.setValueInternal(InterfaceStatus::Online);
});
m_kernel->setOnSerialNumberChanged(
[this](uint32_t newValue)
{
serialNumber.setValueInternal(std::to_string(newValue));
});
m_kernel->setOnHardwareInfoChanged(
[this](Z21::HardwareType type, uint8_t versionMajor, uint8_t versionMinor)
{
hardwareType.setValueInternal(std::string(Z21::toString(type)));
Log::log(*this, LogMessage::I2002_HARDWARE_TYPE_X, hardwareType.value());
if(versionMajor != 0 || versionMinor != 0)
{
firmwareVersion.setValueInternal(std::to_string(versionMajor).append(".").append(std::to_string(versionMinor)));
Log::log(*this, LogMessage::I2003_FIRMWARE_VERSION_X, firmwareVersion.value());
}
else
firmwareVersion.setValueInternal("");
});
m_kernel->setOnTrackPowerOnChanged(
[this](bool powerOn)
{
if(auto w = m_world.lock())
{
if(powerOn == contains(w->state.value(), WorldState::PowerOn))
return;
if(powerOn)
w->powerOn();
else
w->powerOff();
}
});
m_kernel->setOnEmergencyStop(
[this]()
{
if(auto w = m_world.lock(); w && contains(w->state.value(), WorldState::Run))
w->stop();
});
m_kernel->setDecoderController(this);
m_kernel->start();
m_z21PropertyChanged = z21->propertyChanged.connect(
[this](BaseProperty&)
{
m_kernel->setConfig(z21->config());
});
if(auto w = m_world.lock())
{
if(contains(w->state.value(), WorldState::PowerOn))
m_kernel->trackPowerOn();
else
m_kernel->trackPowerOff();
if(!contains(w->state.value(), WorldState::Run))
m_kernel->emergencyStop();
}
Attributes::setEnabled({hostname, port}, false);
}
catch(const LogMessageException& e)
{
status.setValueInternal(InterfaceStatus::Offline);
Log::log(*this, e.message(), e.args());
return false;
}
}
else if(m_kernel && !value)
{
Attributes::setEnabled({hostname, port}, true);
m_z21PropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
status.setValueInternal(InterfaceStatus::Offline);
hardwareType.setValueInternal("");
serialNumber.setValueInternal("");
firmwareVersion.setValueInternal("");
}
return true;
}
void Z21Interface::addToWorld()
{
Interface::addToWorld();
if(auto world = m_world.lock())
{
world->decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
}
}
void Z21Interface::destroying()
{
for(const auto& decoder : *decoders)
{
assert(decoder->interface.value() == std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
decoder->interface = nullptr;
}
if(auto world = m_world.lock())
{
world->decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
}
Interface::destroying();
}
void Z21Interface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
{
switch(event)
{
case WorldEvent::PowerOff:
m_kernel->trackPowerOff();
break;
case WorldEvent::PowerOn:
m_kernel->trackPowerOn();
if(!contains(state, WorldState::Run))
m_kernel->emergencyStop();
break;
case WorldEvent::Stop:
m_kernel->emergencyStop();
break;
case WorldEvent::Run:
if(contains(state, WorldState::PowerOn))
m_kernel->trackPowerOn();
break;
default:
break;
}
}
}
void Z21Interface::idChanged(const std::string& newId)
{
if(m_kernel)
m_kernel->setLogId(newId);
}

Datei anzeigen

@ -0,0 +1,76 @@
/**
* server/src/hardware/interface/z21interface.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_INTERFACE_Z21INTERFACE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_INTERFACE_Z21INTERFACE_HPP
#include "interface.hpp"
#include "../protocol/z21/clientkernel.hpp"
#include "../protocol/z21/clientsettings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../../core/objectproperty.hpp"
/**
* @brief Z21 hardware interface
*/
class Z21Interface final
: public Interface
, public DecoderController
{
CLASS_ID("interface.z21")
DEFAULT_ID("z21")
CREATE(Z21Interface)
private:
std::unique_ptr<Z21::ClientKernel> m_kernel;
boost::signals2::connection m_z21PropertyChanged;
void addToWorld() final;
void destroying() final;
void worldEvent(WorldState state, WorldEvent event) final;
void idChanged(const std::string& newId) final;
void updateVisible();
protected:
bool setOnline(bool& value) final;
public:
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<Z21::ClientSettings> z21;
ObjectProperty<DecoderList> decoders;
Property<std::string> hardwareType;
Property<std::string> serialNumber;
Property<std::string> firmwareVersion;
Z21Interface(const std::weak_ptr<World>& world, std::string_view _id);
// DecoderController:
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
};
#endif

Datei anzeigen

@ -1,64 +0,0 @@
/**
* server/src/hardware/output/keyboard/loconetoutputkeyboard.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "loconetoutputkeyboard.hpp"
#include "../../protocol/loconet/loconet.hpp"
#include "../loconetoutput.hpp"
LocoNetOutputKeyboard::LocoNetOutputKeyboard(std::shared_ptr<LocoNet::LocoNet> loconet) :
OutputKeyboard(),
m_loconet{std::move(loconet)}
{
addressMin.setValueInternal(LocoNetOutput::addressMin);
addressMax.setValueInternal(LocoNetOutput::addressMax);
m_loconet->m_outputKeyboards.emplace_back(this);
}
LocoNetOutputKeyboard::~LocoNetOutputKeyboard()
{
if(auto it = std::find(m_loconet->m_outputKeyboards.begin(), m_loconet->m_outputKeyboards.end(), this); it != m_loconet->m_outputKeyboards.end())
m_loconet->m_outputKeyboards.erase(it);
}
std::vector<OutputKeyboard::OutputInfo> LocoNetOutputKeyboard::getOutputInfo() const
{
std::vector<OutputInfo> outputInfo;
for(auto it : m_loconet->m_outputs)
{
LocoNetOutput& output = *(it.second);
OutputInfo info(output.address, output.id, output.value);
outputInfo.push_back(info);
}
return outputInfo;
}
void LocoNetOutputKeyboard::setOutputValue(uint32_t address, bool value)
{
if(address < LocoNetOutput::addressMin || address > LocoNetOutput::addressMax)
return;
auto it = m_loconet->m_outputs.find(address);
if(it != m_loconet->m_outputs.end())
it->second->value = toTriState(value);
else
m_loconet->send(LocoNet::SwitchRequest(address - 1, value));
}

Datei anzeigen

@ -0,0 +1,55 @@
/**
* server/src/hardware/output/keyboard/outputkeyboard.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "outputkeyboard.hpp"
#include "../output.hpp"
#include "../outputcontroller.hpp"
#include "../../../utils/inrange.hpp"
OutputKeyboard::OutputKeyboard(OutputController& controller)
: Object()
, m_controller{controller}
, addressMin{this, "address_min", m_controller.outputAddressMinMax().first, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, addressMax{this, "address_max", m_controller.outputAddressMinMax().second, PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
}
std::string OutputKeyboard::getObjectId() const
{
return "";
}
std::vector<OutputKeyboard::OutputInfo> OutputKeyboard::getOutputInfo() const
{
std::vector<OutputInfo> outputInfo;
for(auto it : m_controller.outputs())
{
const auto& output = *(it.second);
outputInfo.emplace_back(OutputInfo{output.address, output.id, output.value});
}
return outputInfo;
}
void OutputKeyboard::setOutputValue(uint32_t address, bool value)
{
m_controller.setOutputValue(address, value);
}

Datei anzeigen

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

Datei anzeigen

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

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen