[booster] Added (smart) booster status reading support, see #212
Dieser Commit ist enthalten in:
Ursprung
f8b28fffc3
Commit
0b4ac00777
@ -408,6 +408,11 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
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("hardware:identifications") + "...", [this](){ showObject("world.identifications", Locale::tr("hardware:identifications")); });
|
||||
menu->addAction(Locale::tr("hardware:boosters").append("..."),
|
||||
[this]()
|
||||
{
|
||||
showObject("world.boosters", Locale::tr("hardware:boosters"));
|
||||
});
|
||||
boardsAction = m_menuObjects->addAction(Theme::getIcon("board"), Locale::tr("world:boards") + "...", [this](){ showObject("world.boards", Locale::tr("world:boards")); });
|
||||
m_menuObjects->addAction(
|
||||
Theme::getIcon("zone"),
|
||||
|
||||
@ -107,6 +107,10 @@ QWidget* createWidget(const ObjectPtr& object, QWidget* parent)
|
||||
{
|
||||
return new TileWidget(object, parent);
|
||||
}
|
||||
else if(object->classId() == "booster")
|
||||
{
|
||||
return new TileWidget(object, parent);
|
||||
}
|
||||
else
|
||||
return new ObjectEditWidget(object, parent);
|
||||
}
|
||||
|
||||
@ -83,6 +83,9 @@ file(GLOB SOURCES
|
||||
"src/core/*.hpp"
|
||||
"src/core/*.cpp"
|
||||
"src/enum/*.hpp"
|
||||
"src/hardware/booster/*.cpp"
|
||||
"src/hardware/booster/drivers/*.cpp"
|
||||
"src/hardware/booster/list/*.cpp"
|
||||
"src/hardware/decoder/*.hpp"
|
||||
"src/hardware/decoder/*.cpp"
|
||||
"src/hardware/decoder/list/*.hpp"
|
||||
@ -133,6 +136,7 @@ file(GLOB SOURCES
|
||||
"src/hardware/protocol/loconet/message/uhlenbrock/*.cpp"
|
||||
"src/hardware/protocol/loconet/iohandler/*.hpp"
|
||||
"src/hardware/protocol/loconet/iohandler/*.cpp"
|
||||
"src/hardware/protocol/loconet/lncv/*.cpp"
|
||||
"src/hardware/protocol/marklincan/*.hpp"
|
||||
"src/hardware/protocol/marklincan/*.cpp"
|
||||
"src/hardware/protocol/marklincan/iohandler/*.hpp"
|
||||
|
||||
176
server/src/hardware/booster/booster.cpp
Normale Datei
176
server/src/hardware/booster/booster.cpp
Normale Datei
@ -0,0 +1,176 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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 "booster.hpp"
|
||||
#include "drivers/boosterdriver.hpp"
|
||||
#include "drivers/boosterdrivers.hpp"
|
||||
#include "list/boosterlist.hpp"
|
||||
#include "list/boosterlisttablemodel.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../core/objectproperty.tpp"
|
||||
#include "../../utils/category.hpp"
|
||||
#include "../../utils/contains.hpp"
|
||||
#include "../../utils/displayname.hpp"
|
||||
#include "../../utils/unit.hpp"
|
||||
#include "../../world/world.hpp"
|
||||
|
||||
CREATE_IMPL(Booster)
|
||||
|
||||
Booster::Booster(World& world, std::string_view _id)
|
||||
: IdObject(world, _id)
|
||||
, name{this, "name", std::string(_id), PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly}
|
||||
, type{this, "type", "", PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly, nullptr,
|
||||
[this](std::string& value) -> bool
|
||||
{
|
||||
if(!contains<std::string_view>(BoosterDrivers::types(), value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(auto drv = BoosterDrivers::create(value, *this))
|
||||
{
|
||||
setDriver(std::move(drv));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
, driver{this, "driver", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
|
||||
, load_{this, "load", noValue, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, temperature{this, "temperature", noValue, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, current{this, "current", noValue, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, voltage{this, "voltage", noValue, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
, inputVoltage{this, "inputVoltage", noValue, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly}
|
||||
{
|
||||
const bool editable = contains(m_world.state, WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(name, DisplayName::Object::name);
|
||||
Attributes::addEnabled(name, editable);
|
||||
m_interfaceItems.add(name);
|
||||
|
||||
Attributes::addCustom(type, false);
|
||||
Attributes::addEnabled(type, editable);
|
||||
Attributes::addValues(type, BoosterDrivers::types());
|
||||
Attributes::addAliases(type, BoosterDrivers::types(), BoosterDrivers::names());
|
||||
m_interfaceItems.add(type);
|
||||
|
||||
Attributes::addCategory(load_, Category::status);
|
||||
Attributes::addUnit(load_, Unit::percent);
|
||||
Attributes::addVisible(load_, false);
|
||||
m_interfaceItems.add(load_);
|
||||
|
||||
Attributes::addCategory(temperature, Category::status);
|
||||
Attributes::addUnit(temperature, Unit::degreeCelcius);
|
||||
Attributes::addVisible(temperature, false);
|
||||
m_interfaceItems.add(temperature);
|
||||
|
||||
Attributes::addCategory(current, Category::status);
|
||||
Attributes::addUnit(current, Unit::ampere);
|
||||
Attributes::addVisible(current, false);
|
||||
m_interfaceItems.add(current);
|
||||
|
||||
Attributes::addCategory(voltage, Category::status);
|
||||
Attributes::addUnit(voltage, Unit::volt);
|
||||
Attributes::addVisible(voltage, false);
|
||||
m_interfaceItems.add(voltage);
|
||||
|
||||
Attributes::addCategory(inputVoltage, Category::status);
|
||||
Attributes::addUnit(inputVoltage, Unit::volt);
|
||||
Attributes::addVisible(inputVoltage, false);
|
||||
m_interfaceItems.add(inputVoltage);
|
||||
|
||||
Attributes::addCategory(driver, Category::driver);
|
||||
Attributes::addDisplayName(driver, {});
|
||||
m_interfaceItems.add(driver);
|
||||
|
||||
setDriver(BoosterDrivers::create(BoosterDrivers::types().front(), *this));
|
||||
}
|
||||
|
||||
void Booster::addToWorld()
|
||||
{
|
||||
IdObject::addToWorld();
|
||||
m_world.boosters->addObject(shared_ptr<Booster>());
|
||||
}
|
||||
|
||||
void Booster::destroying()
|
||||
{
|
||||
setDriver(nullptr);
|
||||
m_world.boosters->removeObject(shared_ptr<Booster>());
|
||||
IdObject::destroying();
|
||||
}
|
||||
|
||||
void Booster::load(WorldLoader& loader, const nlohmann::json& data)
|
||||
{
|
||||
if(auto drv = BoosterDrivers::create(data.value<std::string_view>("type", {}), *this)) [[likely]]
|
||||
{
|
||||
setDriver(std::move(drv));
|
||||
IdObject::load(loader, data);
|
||||
}
|
||||
else // unknown driver (this can only happen with corrupt/incorrect files)
|
||||
{
|
||||
auto dataCopy = data;
|
||||
dataCopy.erase("driver"); // clear driver data, so defaults are used
|
||||
IdObject::load(loader, dataCopy);
|
||||
}
|
||||
}
|
||||
|
||||
void Booster::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
IdObject::worldEvent(state, event);
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::EditEnabled:
|
||||
case WorldEvent::EditDisabled:
|
||||
Attributes::setEnabled({name, type}, contains(state, WorldState::Edit));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
driver->worldEvent(state, event);
|
||||
}
|
||||
|
||||
void Booster::setDriver(std::shared_ptr<BoosterDriver> drv)
|
||||
{
|
||||
if(driver)
|
||||
{
|
||||
driver->destroy();
|
||||
}
|
||||
|
||||
// Reset all status values:
|
||||
load_.setValueInternal(noValue);
|
||||
temperature.setValueInternal(noValue);
|
||||
current.setValueInternal(noValue);
|
||||
voltage.setValueInternal(noValue);
|
||||
inputVoltage.setValueInternal(noValue);
|
||||
|
||||
// Show/hide supported values:
|
||||
{
|
||||
using enum BoosterDriver::SupportedStatusValues;
|
||||
const auto mask = drv->supportedStatusValues();
|
||||
Attributes::setVisible(load_, contains(mask, Load));
|
||||
Attributes::setVisible(temperature, contains(mask, Temperature));
|
||||
Attributes::setVisible(current, contains(mask, Current));
|
||||
Attributes::setVisible(voltage, contains(mask, Voltage));
|
||||
Attributes::setVisible(inputVoltage, contains(mask, InputVoltage));
|
||||
}
|
||||
|
||||
Attributes::setDisplayName(driver, drv->getName());
|
||||
driver.setValueInternal(std::move(drv));
|
||||
}
|
||||
61
server/src/hardware/booster/booster.hpp
Normale Datei
61
server/src/hardware/booster/booster.hpp
Normale Datei
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_BOOSTER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_BOOSTER_HPP
|
||||
|
||||
#include "../../core/idobject.hpp"
|
||||
#include "../../core/property.hpp"
|
||||
#include "../../core/objectproperty.hpp"
|
||||
|
||||
class BoosterDriver;
|
||||
|
||||
class Booster : public IdObject
|
||||
{
|
||||
CLASS_ID("booster")
|
||||
DEFAULT_ID("booster")
|
||||
CREATE_DEF(Booster)
|
||||
|
||||
public:
|
||||
static constexpr auto noValue = std::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
Property<std::string> name;
|
||||
Property<std::string> type;
|
||||
ObjectProperty<BoosterDriver> driver;
|
||||
Property<float> load_; // load is already a function
|
||||
Property<float> temperature;
|
||||
Property<float> current;
|
||||
Property<float> voltage;
|
||||
Property<float> inputVoltage;
|
||||
|
||||
Booster(World& world, std::string_view _id);
|
||||
|
||||
protected:
|
||||
void addToWorld() override;
|
||||
void destroying() override;
|
||||
void load(WorldLoader& loader, const nlohmann::json& data) override;
|
||||
void worldEvent(WorldState state, WorldEvent event) override;
|
||||
|
||||
private:
|
||||
void setDriver(std::shared_ptr<BoosterDriver> drv);
|
||||
};
|
||||
|
||||
#endif
|
||||
84
server/src/hardware/booster/drivers/boosterdriver.cpp
Normale Datei
84
server/src/hardware/booster/drivers/boosterdriver.cpp
Normale Datei
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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 "boosterdriver.hpp"
|
||||
#include "../booster.hpp"
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
BoosterDriver::BoosterDriver(Booster& booster)
|
||||
: SubObject(booster, "driver"sv)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& BoosterDriver::boosterName() const
|
||||
{
|
||||
return booster().name.value();
|
||||
}
|
||||
|
||||
void BoosterDriver::invalidateAll()
|
||||
{
|
||||
booster().load_.setValueInternal(Booster::noValue);
|
||||
booster().temperature.setValueInternal(Booster::noValue);
|
||||
booster().current.setValueInternal(Booster::noValue);
|
||||
booster().voltage.setValueInternal(Booster::noValue);
|
||||
booster().inputVoltage.setValueInternal(Booster::noValue);
|
||||
}
|
||||
|
||||
void BoosterDriver::reportLoad(float value)
|
||||
{
|
||||
assert(contains(supportedStatusValues(), SupportedStatusValues::Load));
|
||||
assert(value >= 0 || std::isnan(value));
|
||||
booster().load_.setValueInternal(value);
|
||||
}
|
||||
|
||||
void BoosterDriver::reportTemperature(float value)
|
||||
{
|
||||
assert(contains(supportedStatusValues(), SupportedStatusValues::Temperature));
|
||||
assert(std::isfinite(value) || std::isnan(value));
|
||||
booster().temperature.setValueInternal(value);
|
||||
}
|
||||
|
||||
void BoosterDriver::reportCurrent(float value)
|
||||
{
|
||||
assert(contains(supportedStatusValues(), SupportedStatusValues::Current));
|
||||
assert(value >= 0 || std::isnan(value));
|
||||
booster().current.setValueInternal(value);
|
||||
}
|
||||
|
||||
void BoosterDriver::reportVoltage(float value)
|
||||
{
|
||||
assert(contains(supportedStatusValues(), SupportedStatusValues::Voltage));
|
||||
assert(value >= 0 || std::isnan(value));
|
||||
booster().voltage.setValueInternal(value);
|
||||
}
|
||||
|
||||
void BoosterDriver::reportInputVoltage(float value)
|
||||
{
|
||||
assert(contains(supportedStatusValues(), SupportedStatusValues::InputVoltage));
|
||||
assert(value >= 0 || std::isnan(value));
|
||||
booster().inputVoltage.setValueInternal(value);
|
||||
}
|
||||
|
||||
Booster& BoosterDriver::booster() const
|
||||
{
|
||||
return static_cast<Booster&>(parent());
|
||||
}
|
||||
106
server/src/hardware/booster/drivers/boosterdriver.hpp
Normale Datei
106
server/src/hardware/booster/drivers/boosterdriver.hpp
Normale Datei
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_DRIVERS_BOOSTERDRIVER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_BOOSTERDRIVER_HPP
|
||||
|
||||
#include "../../../core/subobject.hpp"
|
||||
|
||||
#define BOOSTER_DRIVER_CREATE_DEF \
|
||||
public: \
|
||||
static std::shared_ptr<BoosterDriver> create(Booster& booster)
|
||||
|
||||
#define BOOSTER_DRIVER_CREATE_IMPL(T) \
|
||||
std::shared_ptr<BoosterDriver> T::create(Booster& booster) \
|
||||
{ \
|
||||
return std::make_shared<T>(booster); \
|
||||
}
|
||||
|
||||
#define BOOSTER_DRIVER_CREATE(T) \
|
||||
public: \
|
||||
static std::shared_ptr<BoosterDriver> create(Booster& booster) \
|
||||
{ \
|
||||
return std::make_shared<T>(booster); \
|
||||
}
|
||||
|
||||
#define BOOSTER_DRIVER_NAME(Name) \
|
||||
public: \
|
||||
static constexpr std::string_view name = Name; \
|
||||
std::string_view getName() const override { return name; }
|
||||
|
||||
class Booster;
|
||||
|
||||
class BoosterDriver : public SubObject
|
||||
{
|
||||
public:
|
||||
enum class SupportedStatusValues
|
||||
{
|
||||
Load = 1 << 0,
|
||||
Temperature = 1 << 1,
|
||||
Current = 1 << 2,
|
||||
Voltage = 1 << 3,
|
||||
InputVoltage = 1 << 4,
|
||||
};
|
||||
|
||||
virtual std::string_view getName() const = 0;
|
||||
virtual SupportedStatusValues supportedStatusValues() const = 0;
|
||||
|
||||
virtual void worldEvent(WorldState /*state*/, WorldEvent /*event*/)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
static constexpr auto noValue = std::numeric_limits<float>::quiet_NaN();
|
||||
|
||||
BoosterDriver(Booster& booster);
|
||||
|
||||
const std::string& boosterName() const;
|
||||
|
||||
void invalidateAll();
|
||||
void reportLoad(float value = noValue);
|
||||
void reportTemperature(float value = noValue);
|
||||
void reportCurrent(float value = noValue);
|
||||
void reportVoltage(float value = noValue);
|
||||
void reportInputVoltage(float value = noValue);
|
||||
|
||||
private:
|
||||
Booster& booster() const;
|
||||
};
|
||||
|
||||
|
||||
constexpr BoosterDriver::SupportedStatusValues operator| (BoosterDriver::SupportedStatusValues lhs, BoosterDriver::SupportedStatusValues rhs)
|
||||
{
|
||||
using UT = std::underlying_type_t<BoosterDriver::SupportedStatusValues>;
|
||||
return static_cast<BoosterDriver::SupportedStatusValues>(static_cast<UT>(lhs) | static_cast<UT>(rhs));
|
||||
}
|
||||
|
||||
constexpr void operator|= (BoosterDriver::SupportedStatusValues& lhs, BoosterDriver::SupportedStatusValues rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
}
|
||||
|
||||
constexpr bool contains(BoosterDriver::SupportedStatusValues value, BoosterDriver::SupportedStatusValues mask)
|
||||
{
|
||||
using UT = std::underlying_type_t<BoosterDriver::SupportedStatusValues>;
|
||||
return (static_cast<UT>(value) & static_cast<UT>(mask)) == static_cast<UT>(mask);
|
||||
}
|
||||
|
||||
#endif
|
||||
58
server/src/hardware/booster/drivers/boosterdrivers.cpp
Normale Datei
58
server/src/hardware/booster/drivers/boosterdrivers.cpp
Normale Datei
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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 "boosterdrivers.hpp"
|
||||
#include "../../../utils/stripprefix.hpp"
|
||||
|
||||
#include "dr5033boosterdriver.hpp"
|
||||
#include "power4boosterdriver.hpp"
|
||||
|
||||
#define BOOSTER_DRIVERS \
|
||||
BOOSTER_DRIVER(DR5033BoosterDriver) \
|
||||
BOOSTER_DRIVER(Power4BoosterDriver)
|
||||
|
||||
std::span<const std::string_view> BoosterDrivers::types()
|
||||
{
|
||||
static constexpr auto values = std::array{
|
||||
#define BOOSTER_DRIVER(T) stripPrefix(T::classId, classIdPrefix),
|
||||
BOOSTER_DRIVERS
|
||||
#undef BOOSTER_DRIVER
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
std::span<const std::string_view> BoosterDrivers::names()
|
||||
{
|
||||
static constexpr auto values = std::array{
|
||||
#define BOOSTER_DRIVER(T) T::name,
|
||||
BOOSTER_DRIVERS
|
||||
#undef BOOSTER_DRIVER
|
||||
};
|
||||
return values;
|
||||
}
|
||||
|
||||
std::shared_ptr<BoosterDriver> BoosterDrivers::create(std::string_view typeId, Booster& booster)
|
||||
{
|
||||
#define BOOSTER_DRIVER(T) if(typeId == stripPrefix(T::classId, classIdPrefix)) { return std::make_shared<T>(booster); }
|
||||
BOOSTER_DRIVERS
|
||||
#undef BOOSTER_DRIVER
|
||||
return {};
|
||||
}
|
||||
41
server/src/hardware/booster/drivers/boosterdrivers.hpp
Normale Datei
41
server/src/hardware/booster/drivers/boosterdrivers.hpp
Normale Datei
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_DRIVERS_BOOSTERDRIVERS_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_BOOSTERDRIVERS_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
|
||||
class Booster;
|
||||
class BoosterDriver;
|
||||
|
||||
struct BoosterDrivers
|
||||
{
|
||||
static constexpr std::string_view classIdPrefix = "booster_driver.";
|
||||
|
||||
static std::span<const std::string_view> types();
|
||||
static std::span<const std::string_view> names();
|
||||
static std::shared_ptr<BoosterDriver> create(std::string_view typeId, Booster& booster);
|
||||
};
|
||||
|
||||
#endif
|
||||
47
server/src/hardware/booster/drivers/dr5033boosterdriver.hpp
Normale Datei
47
server/src/hardware/booster/drivers/dr5033boosterdriver.hpp
Normale Datei
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_DRIVERS_DR5033BOOSTERDRIVER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_DR5033BOOSTERDRIVER_HPP
|
||||
|
||||
#include "loconetlncvboosterdriver.hpp"
|
||||
|
||||
class DR5033BoosterDriver final : public LocoNetLNCVBoosterDriver
|
||||
{
|
||||
CLASS_ID("booster_driver.dr5033");
|
||||
BOOSTER_DRIVER_CREATE(DR5033BoosterDriver);
|
||||
BOOSTER_DRIVER_NAME("Digikeijs DR5033");
|
||||
|
||||
public:
|
||||
DR5033BoosterDriver(Booster& booster)
|
||||
: LocoNetLNCVBoosterDriver(booster, 5033)
|
||||
{
|
||||
}
|
||||
|
||||
SupportedStatusValues supportedStatusValues() const final
|
||||
{
|
||||
using enum SupportedStatusValues;
|
||||
return (Load | Temperature);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
111
server/src/hardware/booster/drivers/loconetboosterdriver.cpp
Normale Datei
111
server/src/hardware/booster/drivers/loconetboosterdriver.cpp
Normale Datei
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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 "loconetboosterdriver.hpp"
|
||||
#include "../../interface/loconetinterface.hpp"
|
||||
#include "../../../core/attributes.hpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
#include "../../../world/getworld.hpp"
|
||||
#include "../../../world/world.hpp"
|
||||
|
||||
LocoNetBoosterDriver::LocoNetBoosterDriver(Booster& booster)
|
||||
: BoosterDriver(booster)
|
||||
, interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript, nullptr,
|
||||
[this](std::shared_ptr<LocoNetInterface> value)
|
||||
{
|
||||
if(interface)
|
||||
{
|
||||
m_interfacePropertyChanged.disconnect();
|
||||
}
|
||||
if(value)
|
||||
{
|
||||
m_interfacePropertyChanged = value->propertyChanged.connect(std::bind_front(&LocoNetBoosterDriver::interfacePropertyChanged, this));
|
||||
}
|
||||
return true;
|
||||
}}
|
||||
{
|
||||
Attributes::addDisplayName(interface, DisplayName::Hardware::interface);
|
||||
Attributes::addEnabled(interface, false);
|
||||
Attributes::addObjectList(interface, getWorld(*this).loconetInterfaces);
|
||||
m_interfaceItems.add(interface);
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::destroying()
|
||||
{
|
||||
BoosterDriver::destroying();
|
||||
m_interfacePropertyChanged.disconnect();
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::loaded()
|
||||
{
|
||||
BoosterDriver::loaded();
|
||||
|
||||
if(interface)
|
||||
{
|
||||
m_interfacePropertyChanged = interface->propertyChanged.connect(std::bind_front(&LocoNetBoosterDriver::interfacePropertyChanged, this));
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
BoosterDriver::worldEvent(state, event);
|
||||
switch(event)
|
||||
{
|
||||
case WorldEvent::EditDisabled:
|
||||
case WorldEvent::EditEnabled:
|
||||
updateEnabled();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::interfaceOnlineChanged(bool value)
|
||||
{
|
||||
if(!value) // offline
|
||||
{
|
||||
invalidateAll();
|
||||
}
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::interfacePropertyChanged(BaseProperty& property)
|
||||
{
|
||||
if(property.name() == "online")
|
||||
{
|
||||
interfaceOnlineChanged(static_cast<Property<bool>&>(property).value());
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::updateEnabled()
|
||||
{
|
||||
const bool editable = contains(getWorld(*this).state, WorldState::Edit);
|
||||
const bool online = interface && interface->online;
|
||||
|
||||
updateEnabled(editable, online);
|
||||
}
|
||||
|
||||
void LocoNetBoosterDriver::updateEnabled(bool editable, bool online)
|
||||
{
|
||||
Attributes::setEnabled(interface, editable && !online);
|
||||
}
|
||||
54
server/src/hardware/booster/drivers/loconetboosterdriver.hpp
Normale Datei
54
server/src/hardware/booster/drivers/loconetboosterdriver.hpp
Normale Datei
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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_BOOSTER_DRIVERS_LOCONETBOOSTERDRIVER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_LOCONETBOOSTERDRIVER_HPP
|
||||
|
||||
#include "boosterdriver.hpp"
|
||||
#include "../../../core/objectproperty.hpp"
|
||||
|
||||
class LocoNetInterface;
|
||||
|
||||
class LocoNetBoosterDriver : public BoosterDriver
|
||||
{
|
||||
public:
|
||||
ObjectProperty<LocoNetInterface> interface;
|
||||
|
||||
void worldEvent(WorldState state, WorldEvent event) override;
|
||||
|
||||
protected:
|
||||
LocoNetBoosterDriver(Booster& booster);
|
||||
|
||||
void destroying() override;
|
||||
void loaded() override;
|
||||
|
||||
virtual void interfaceOnlineChanged(bool value);
|
||||
|
||||
void updateEnabled();
|
||||
virtual void updateEnabled(bool editable, bool online);
|
||||
|
||||
private:
|
||||
boost::signals2::connection m_interfacePropertyChanged;
|
||||
|
||||
void interfacePropertyChanged(BaseProperty& property);
|
||||
};
|
||||
|
||||
#endif
|
||||
188
server/src/hardware/booster/drivers/loconetlncvboosterdriver.cpp
Normale Datei
188
server/src/hardware/booster/drivers/loconetlncvboosterdriver.cpp
Normale Datei
@ -0,0 +1,188 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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 "loconetlncvboosterdriver.hpp"
|
||||
#include "../../interface/loconetinterface.hpp"
|
||||
#include "../../../core/attributes.hpp"
|
||||
#include "../../../core/eventloop.hpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../log/log.hpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
#include "../../../utils/unit.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::chrono::milliseconds startDelay(uint16_t address, uint16_t period)
|
||||
{
|
||||
constexpr uint32_t prime = 2654435761u; // Knuth
|
||||
return std::chrono::milliseconds((address * prime) % (1000 * period));
|
||||
}
|
||||
}
|
||||
|
||||
LocoNetLNCVBoosterDriver::LocoNetLNCVBoosterDriver(Booster& booster, uint16_t moduleId)
|
||||
: LocoNetBoosterDriver(booster)
|
||||
, address{this, "address", addressDefault, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript}
|
||||
, pollInterval{this, "poll_interval", pollIntervalDefault, PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::NoScript}
|
||||
, m_moduleId{moduleId}
|
||||
, m_pollTimer{EventLoop::ioContext}
|
||||
{
|
||||
Attributes::addDisplayName(address, DisplayName::Hardware::address);
|
||||
Attributes::addEnabled(address, false);
|
||||
Attributes::addMinMax(address, addressMin, addressMax);
|
||||
m_interfaceItems.add(address);
|
||||
|
||||
Attributes::addDisplayName(pollInterval, "booster_driver:poll_interval");
|
||||
Attributes::addEnabled(pollInterval, false);
|
||||
Attributes::addMinMax(pollInterval, pollIntervalMin, pollIntervalMax);
|
||||
Attributes::addUnit(pollInterval, Unit::seconds);
|
||||
m_interfaceItems.add(pollInterval);
|
||||
|
||||
LocoNetBoosterDriver::updateEnabled();
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::destroying()
|
||||
{
|
||||
LocoNetBoosterDriver::destroying();
|
||||
m_pollTimer.cancel();
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::interfaceOnlineChanged(bool value)
|
||||
{
|
||||
LocoNetBoosterDriver::interfaceOnlineChanged(value);
|
||||
|
||||
if(value) // online
|
||||
{
|
||||
m_softwareVersion.retries = 0;
|
||||
m_temperature.retries = 0;
|
||||
m_load.retries = 0;
|
||||
|
||||
// start poll timer with an address-based phase offset to spread bus load:
|
||||
m_pollTimer.expires_after(startDelay(address, pollInterval));
|
||||
m_pollTimer.async_wait(std::bind_front(&LocoNetLNCVBoosterDriver::poll, weak_ptr<LocoNetLNCVBoosterDriver>()));
|
||||
}
|
||||
else // offline
|
||||
{
|
||||
m_pollTimer.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::updateEnabled(bool editable, bool online)
|
||||
{
|
||||
LocoNetBoosterDriver::updateEnabled(editable, online);
|
||||
Attributes::setEnabled(address, editable && !online);
|
||||
Attributes::setEnabled(pollInterval, editable);
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::poll(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, std::error_code ec)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(auto self = weak.lock())
|
||||
{
|
||||
if(self->m_softwareVersion.pollEnabled())
|
||||
{
|
||||
self->interface->readLNCV(self->m_moduleId, self->address, self->m_softwareVersion.lncv, std::bind_front(&LocoNetLNCVBoosterDriver::softwareVersionResponse, weak));
|
||||
}
|
||||
if(self->m_temperature.pollEnabled())
|
||||
{
|
||||
self->interface->readLNCV(self->m_moduleId, self->address, self->m_temperature.lncv, std::bind_front(&LocoNetLNCVBoosterDriver::temperatureResponse, weak));
|
||||
}
|
||||
if(self->m_load.pollEnabled())
|
||||
{
|
||||
self->interface->readLNCV(self->m_moduleId, self->address, self->m_load.lncv, std::bind_front(&LocoNetLNCVBoosterDriver::loadResponse, weak));
|
||||
}
|
||||
|
||||
if(self->m_temperature.pollEnabled() || self->m_load.pollEnabled())
|
||||
{
|
||||
self->m_pollTimer.expires_at(self->m_pollTimer.expiry() + std::chrono::seconds(self->pollInterval.value()));
|
||||
self->m_pollTimer.async_wait(std::bind_front(&LocoNetLNCVBoosterDriver::poll, weak));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::softwareVersionResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec)
|
||||
{
|
||||
if(auto self = weak.lock())
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
Log::log(self->parent(), LogMessage::I2006_BOOSTER_X_SOFTWARE_VERSION_X, self->boosterName(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::log(self->parent(), LogMessage::E2025_READING_BOOSTER_X_SOFTWARE_VERSION_FAILED_X, self->boosterName(), ec.message());
|
||||
}
|
||||
self->m_softwareVersion.retries = retryLimit; // stop reading, try just once
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::temperatureResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec)
|
||||
{
|
||||
if(auto self = weak.lock())
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
self->m_temperature.retries = 0;
|
||||
self->reportTemperature(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->m_temperature.retries++;
|
||||
self->reportTemperature(); // invalidate value
|
||||
|
||||
Log::log(
|
||||
self->parent(),
|
||||
self->m_temperature.pollEnabled()
|
||||
? LogMessage::W2026_READING_BOOSTER_X_TEMPERATURE_FAILED_X // keep trying -> warning
|
||||
: LogMessage::E2026_READING_BOOSTER_X_TEMPERATURE_FAILED_X, // give up -> error
|
||||
self->boosterName(),
|
||||
ec.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocoNetLNCVBoosterDriver::loadResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec)
|
||||
{
|
||||
if(auto self = weak.lock())
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
self->m_load.retries = 0;
|
||||
self->reportLoad(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->m_load.retries++;
|
||||
self->reportLoad(); // invalidate value
|
||||
|
||||
Log::log(
|
||||
self->parent(),
|
||||
self->m_load.pollEnabled()
|
||||
? LogMessage::W2027_READING_BOOSTER_X_LOAD_FAILED_X // keep trying -> warning
|
||||
: LogMessage::E2027_READING_BOOSTER_X_LOAD_FAILED_X, // give up -> error
|
||||
self->boosterName(),
|
||||
ec.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
80
server/src/hardware/booster/drivers/loconetlncvboosterdriver.hpp
Normale Datei
80
server/src/hardware/booster/drivers/loconetlncvboosterdriver.hpp
Normale Datei
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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_BOOSTER_DRIVERS_LOCONETLNCVBOOSTERDRIVER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_LOCONETLNCVBOOSTERDRIVER_HPP
|
||||
|
||||
#include "loconetboosterdriver.hpp"
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include "../../../core/property.hpp"
|
||||
|
||||
class LocoNetLNCVBoosterDriver : public LocoNetBoosterDriver
|
||||
{
|
||||
public:
|
||||
Property<uint16_t> address;
|
||||
Property<uint16_t> pollInterval;
|
||||
|
||||
protected:
|
||||
struct PollValue
|
||||
{
|
||||
uint16_t lncv;
|
||||
bool enabled = true;
|
||||
uint8_t retries = 0;
|
||||
|
||||
inline bool pollEnabled() const
|
||||
{
|
||||
return enabled && (retries < retryLimit);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr uint8_t retryLimit = 3;
|
||||
|
||||
const uint16_t m_moduleId;
|
||||
PollValue m_softwareVersion{1};
|
||||
PollValue m_temperature{6};
|
||||
PollValue m_load{7};
|
||||
|
||||
LocoNetLNCVBoosterDriver(Booster& booster, uint16_t moduleId);
|
||||
|
||||
void destroying() override;
|
||||
|
||||
void interfaceOnlineChanged(bool value) final;
|
||||
void updateEnabled(bool editable, bool online) final;
|
||||
|
||||
private:
|
||||
static constexpr uint16_t addressMin = 0;
|
||||
static constexpr uint16_t addressDefault = 1;
|
||||
static constexpr uint16_t addressMax = 65534;
|
||||
static constexpr uint16_t pollIntervalMin = 1;
|
||||
static constexpr uint16_t pollIntervalDefault = 5;
|
||||
static constexpr uint16_t pollIntervalMax = 30;
|
||||
|
||||
boost::asio::steady_timer m_pollTimer;
|
||||
|
||||
static void poll(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weakSelf, std::error_code ec);
|
||||
|
||||
static void softwareVersionResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec);
|
||||
static void temperatureResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec);
|
||||
static void loadResponse(const std::weak_ptr<LocoNetLNCVBoosterDriver>& weak, uint16_t value, std::error_code ec);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
46
server/src/hardware/booster/drivers/power4boosterdriver.hpp
Normale Datei
46
server/src/hardware/booster/drivers/power4boosterdriver.hpp
Normale Datei
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_DRIVERS_POWER4BOOSTERDRIVER_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_DRIVERS_POWER4BOOSTERDRIVER_HPP
|
||||
|
||||
#include "loconetlncvboosterdriver.hpp"
|
||||
|
||||
class Power4BoosterDriver final : public LocoNetLNCVBoosterDriver
|
||||
{
|
||||
CLASS_ID("booster_driver.power4");
|
||||
BOOSTER_DRIVER_CREATE(Power4BoosterDriver);
|
||||
BOOSTER_DRIVER_NAME("Uhlenbrock Power 4");
|
||||
|
||||
public:
|
||||
Power4BoosterDriver(Booster& booster)
|
||||
: LocoNetLNCVBoosterDriver(booster, 6324)
|
||||
{
|
||||
}
|
||||
|
||||
SupportedStatusValues supportedStatusValues() const final
|
||||
{
|
||||
using enum SupportedStatusValues;
|
||||
return (Load | Temperature);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
71
server/src/hardware/booster/list/boosterlist.cpp
Normale Datei
71
server/src/hardware/booster/list/boosterlist.cpp
Normale Datei
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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 "boosterlist.hpp"
|
||||
#include "boosterlisttablemodel.hpp"
|
||||
#include "../booster.hpp"
|
||||
#include "../../../world/world.hpp"
|
||||
#include "../../../world/getworld.hpp"
|
||||
#include "../../../core/attributes.hpp"
|
||||
#include "../../../core/method.tpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
|
||||
BoosterList::BoosterList(Object& _parent, std::string_view parentPropertyName) :
|
||||
ObjectList<Booster>(_parent, parentPropertyName),
|
||||
create{*this, "create",
|
||||
[this]()
|
||||
{
|
||||
auto& world = getWorld(parent());
|
||||
return Booster::create(world, world.getUniqueId(Booster::defaultId));
|
||||
}},
|
||||
delete_{*this, "delete", std::bind(&BoosterList::deleteMethodHandler, this, std::placeholders::_1)}
|
||||
{
|
||||
const bool editable = contains(getWorld(parent()).state.value(), WorldState::Edit);
|
||||
|
||||
Attributes::addDisplayName(create, DisplayName::List::create);
|
||||
Attributes::addEnabled(create, editable);
|
||||
m_interfaceItems.add(create);
|
||||
|
||||
Attributes::addDisplayName(delete_, DisplayName::List::delete_);
|
||||
Attributes::addEnabled(delete_, editable);
|
||||
m_interfaceItems.add(delete_);
|
||||
}
|
||||
|
||||
TableModelPtr BoosterList::getModel()
|
||||
{
|
||||
return std::make_shared<BoosterListTableModel>(*this);
|
||||
}
|
||||
|
||||
void BoosterList::worldEvent(WorldState state, WorldEvent event)
|
||||
{
|
||||
ObjectList<Booster>::worldEvent(state, event);
|
||||
|
||||
const bool editable = contains(state, WorldState::Edit);
|
||||
|
||||
Attributes::setEnabled(create, editable);
|
||||
Attributes::setEnabled(delete_, editable);
|
||||
}
|
||||
|
||||
bool BoosterList::isListedProperty(std::string_view name)
|
||||
{
|
||||
return BoosterListTableModel::isListedProperty(name);
|
||||
}
|
||||
47
server/src/hardware/booster/list/boosterlist.hpp
Normale Datei
47
server/src/hardware/booster/list/boosterlist.hpp
Normale Datei
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_LIST_BOOSTERLIST_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_LIST_BOOSTERLIST_HPP
|
||||
|
||||
#include "../../../core/objectlist.hpp"
|
||||
#include "../../../core/method.hpp"
|
||||
#include "../booster.hpp"
|
||||
|
||||
class BoosterList final : public ObjectList<Booster>
|
||||
{
|
||||
CLASS_ID("list.booster")
|
||||
|
||||
protected:
|
||||
void worldEvent(WorldState state, WorldEvent event) final;
|
||||
bool isListedProperty(std::string_view name) final;
|
||||
|
||||
public:
|
||||
Method<std::shared_ptr<Booster>()> create;
|
||||
Method<void(const std::shared_ptr<Booster>&)> delete_;
|
||||
|
||||
BoosterList(Object& _parent, std::string_view parentPropertyName);
|
||||
~BoosterList() final = default;
|
||||
|
||||
TableModelPtr getModel() final;
|
||||
};
|
||||
|
||||
#endif
|
||||
75
server/src/hardware/booster/list/boosterlisttablemodel.cpp
Normale Datei
75
server/src/hardware/booster/list/boosterlisttablemodel.cpp
Normale Datei
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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 "boosterlisttablemodel.hpp"
|
||||
#include "boosterlist.hpp"
|
||||
#include "../../../core/objectproperty.tpp"
|
||||
#include "../../../utils/displayname.hpp"
|
||||
|
||||
constexpr uint32_t columnId = 0;
|
||||
constexpr uint32_t columnName = 1;
|
||||
|
||||
bool BoosterListTableModel::isListedProperty(std::string_view name)
|
||||
{
|
||||
return
|
||||
name == "id" ||
|
||||
name == "name";
|
||||
}
|
||||
|
||||
BoosterListTableModel::BoosterListTableModel(BoosterList& list) :
|
||||
ObjectListTableModel<Booster>(list)
|
||||
{
|
||||
setColumnHeaders({
|
||||
DisplayName::Object::id,
|
||||
DisplayName::Object::name
|
||||
});
|
||||
}
|
||||
|
||||
std::string BoosterListTableModel::getText(uint32_t column, uint32_t row) const
|
||||
{
|
||||
if(row < rowCount())
|
||||
{
|
||||
const Booster& booster = getItem(row);
|
||||
|
||||
switch(column)
|
||||
{
|
||||
case columnId:
|
||||
return booster.id;
|
||||
|
||||
case columnName:
|
||||
return booster.name;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void BoosterListTableModel::propertyChanged(BaseProperty& property, uint32_t row)
|
||||
{
|
||||
if(property.name() == "id")
|
||||
changed(row, columnId);
|
||||
else if(property.name() == "name")
|
||||
changed(row, columnName);
|
||||
}
|
||||
47
server/src/hardware/booster/list/boosterlisttablemodel.hpp
Normale Datei
47
server/src/hardware/booster/list/boosterlisttablemodel.hpp
Normale Datei
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 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_BOOSTER_LIST_BOOSTERLISTTABLEMODEL_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_BOOSTER_LIST_BOOSTERLISTTABLEMODEL_HPP
|
||||
|
||||
#include "../../../core/objectlisttablemodel.hpp"
|
||||
#include "boosterlist.hpp"
|
||||
|
||||
class BoosterList;
|
||||
|
||||
class BoosterListTableModel : public ObjectListTableModel<Booster>
|
||||
{
|
||||
friend class BoosterList;
|
||||
|
||||
protected:
|
||||
void propertyChanged(BaseProperty& property, uint32_t row) final;
|
||||
|
||||
public:
|
||||
CLASS_ID("table_model.list.booster")
|
||||
|
||||
static bool isListedProperty(std::string_view name);
|
||||
|
||||
BoosterListTableModel(BoosterList& boosterList);
|
||||
|
||||
std::string getText(uint32_t column, uint32_t row) const final;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -38,6 +38,7 @@
|
||||
#include "../protocol/loconet/iohandler/lbserveriohandler.hpp"
|
||||
#include "../protocol/loconet/iohandler/z21iohandler.hpp"
|
||||
#include "../../core/attributes.hpp"
|
||||
#include "../../core/controllerlist.hpp"
|
||||
#include "../../core/eventloop.hpp"
|
||||
#include "../../core/method.tpp"
|
||||
#include "../../core/objectproperty.tpp"
|
||||
@ -136,6 +137,14 @@ bool LocoNetInterface::immPacket(std::span<uint8_t> dccPacket, uint8_t repeat)
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocoNetInterface::readLNCV(uint16_t moduleId, uint16_t address, uint16_t lncv, std::function<void(uint16_t, std::error_code)> callback)
|
||||
{
|
||||
if(m_kernel)
|
||||
{
|
||||
m_kernel->readLNCV(moduleId, address, lncv, std::move(callback));
|
||||
}
|
||||
}
|
||||
|
||||
std::span<const DecoderProtocol> LocoNetInterface::decoderProtocols() const
|
||||
{
|
||||
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
|
||||
@ -415,6 +424,7 @@ void LocoNetInterface::addToWorld()
|
||||
OutputController::addToWorld(outputListColumns);
|
||||
IdentificationController::addToWorld(identificationListColumns);
|
||||
LNCVProgrammingController::addToWorld();
|
||||
m_world.loconetInterfaces->add(Object::shared_ptr<LocoNetInterface>());
|
||||
}
|
||||
|
||||
void LocoNetInterface::loaded()
|
||||
@ -426,6 +436,7 @@ void LocoNetInterface::loaded()
|
||||
|
||||
void LocoNetInterface::destroying()
|
||||
{
|
||||
m_world.loconetInterfaces->remove(Object::shared_ptr<LocoNetInterface>());
|
||||
LNCVProgrammingController::destroying();
|
||||
IdentificationController::destroying();
|
||||
OutputController::destroying();
|
||||
|
||||
@ -92,6 +92,8 @@ class LocoNetInterface final
|
||||
//! \return \c true if send to command station, \c false otherwise.
|
||||
bool immPacket(std::span<uint8_t> dccPacket, uint8_t repeat);
|
||||
|
||||
void readLNCV(uint16_t moduleId, uint16_t address, uint16_t lncv, std::function<void(uint16_t, std::error_code)> callback);
|
||||
|
||||
// DecoderController:
|
||||
std::span<const DecoderProtocol> decoderProtocols() const final;
|
||||
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
* Copyright (C) 2021-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -33,6 +33,7 @@ struct Config
|
||||
{
|
||||
static constexpr uint16_t timeoutMin = 100; //!< Minimum timeout in milliseconds
|
||||
static constexpr uint16_t timeoutMax = 10000; //!< Maximum timeout in milliseconds
|
||||
static constexpr uint16_t lncvReadResponseTimeout = 100;
|
||||
|
||||
uint16_t echoTimeout; //!< Wait for echo timeout in milliseconds
|
||||
uint16_t responseTimeout; //!< Wait for response timeout in milliseconds
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2026 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,6 +22,7 @@
|
||||
|
||||
#include "kernel.hpp"
|
||||
#include "iohandler/iohandler.hpp"
|
||||
#include "lncv/lncvutils.hpp"
|
||||
#include "messages.hpp"
|
||||
#include "../../decoder/decoder.hpp"
|
||||
#include "../../decoder/decoderchangeflags.hpp"
|
||||
@ -629,21 +630,49 @@ void Kernel::receive(const Message& message)
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(m_lncvActive && m_onLNCVReadResponse &&
|
||||
longAck.respondingOpCode() == OPC_IMM_PACKET && longAck.ack1 == 0x7F &&
|
||||
Uhlenbrock::LNCVWrite::check(lastSentMessage()))
|
||||
else if(longAck.respondingOpCode() == OPC_IMM_PACKET)
|
||||
{
|
||||
const auto& lncvWrite = static_cast<const Uhlenbrock::LNCVWrite&>(lastSentMessage());
|
||||
if(lncvWrite.lncv() == 0)
|
||||
if(m_waitingForLNCVReadResponse)
|
||||
{
|
||||
m_lncvModuleAddress = lncvWrite.value();
|
||||
}
|
||||
|
||||
EventLoop::call(
|
||||
[this, lncvWrite]()
|
||||
if(!m_lncvReads.empty() &&
|
||||
m_lncvReads.front().moduleId == m_pendingLNCVRead.moduleId &&
|
||||
m_lncvReads.front().address == m_pendingLNCVRead.address &&
|
||||
m_lncvReads.front().lncv == m_pendingLNCVRead.lncv)
|
||||
{
|
||||
m_onLNCVReadResponse(true, lncvWrite.lncv(), lncvWrite.value());
|
||||
});
|
||||
m_pendingLNCVRead.reset();
|
||||
auto ec = LNCV::parseLongAck(longAck.ack1);
|
||||
if(!ec)
|
||||
{
|
||||
// 0x7F indicates a successful LONG_ACK, but for a read operation
|
||||
// we expect a proper read response message instead. Receiving
|
||||
// only a LONG_ACK is unexpected, so we treat it as
|
||||
// unexpected response until proven otherwise.
|
||||
ec = LNCV::Error::unexpectedResponse();
|
||||
}
|
||||
EventLoop::call(
|
||||
[callback=std::move(m_lncvReads.front().callback), ec]()
|
||||
{
|
||||
callback(0, ec);
|
||||
});
|
||||
m_lncvReads.pop();
|
||||
}
|
||||
}
|
||||
else if(m_lncvActive && m_onLNCVReadResponse &&
|
||||
longAck.ack1 == 0x7F &&
|
||||
Uhlenbrock::LNCVWrite::check(lastSentMessage()))
|
||||
{
|
||||
const auto& lncvWrite = static_cast<const Uhlenbrock::LNCVWrite&>(lastSentMessage());
|
||||
if(lncvWrite.lncv() == 0)
|
||||
{
|
||||
m_lncvModuleAddress = lncvWrite.value();
|
||||
}
|
||||
|
||||
EventLoop::call(
|
||||
[this, lncvWrite]()
|
||||
{
|
||||
m_onLNCVReadResponse(true, lncvWrite.lncv(), lncvWrite.value());
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -810,20 +839,38 @@ void Kernel::receive(const Message& message)
|
||||
{
|
||||
[[maybe_unused]] const auto& readSpecialOptionReply = static_cast<const Uhlenbrock::ReadSpecialOptionReply&>(message);
|
||||
}
|
||||
else if(m_onLNCVReadResponse && Uhlenbrock::LNCVReadResponse::check(message))
|
||||
else if(Uhlenbrock::LNCVReadResponse::check(message))
|
||||
{
|
||||
const auto& lncvReadResponse = static_cast<const Uhlenbrock::LNCVReadResponse&>(message);
|
||||
if(lncvReadResponse.lncv() == 0)
|
||||
|
||||
if(!m_lncvReads.empty() &&
|
||||
m_lncvReads.front().moduleId == lncvReadResponse.moduleId() &&
|
||||
m_lncvReads.front().address == m_pendingLNCVRead.address &&
|
||||
m_lncvReads.front().lncv == lncvReadResponse.lncv())
|
||||
{
|
||||
m_lncvActive = true;
|
||||
m_lncvModuleAddress = lncvReadResponse.value();
|
||||
EventLoop::call(
|
||||
[callback=m_lncvReads.front().callback, value=lncvReadResponse.value()]()
|
||||
{
|
||||
callback(value, {});
|
||||
});
|
||||
m_lncvReads.pop();
|
||||
}
|
||||
else if(m_onLNCVReadResponse)
|
||||
{
|
||||
if(lncvReadResponse.lncv() == 0)
|
||||
{
|
||||
m_lncvActive = true;
|
||||
m_lncvModuleAddress = lncvReadResponse.value();
|
||||
}
|
||||
|
||||
EventLoop::call(
|
||||
[this, lncvReadResponse]()
|
||||
{
|
||||
m_onLNCVReadResponse(true, lncvReadResponse.lncv(), lncvReadResponse.value());
|
||||
});
|
||||
}
|
||||
|
||||
EventLoop::call(
|
||||
[this, lncvReadResponse]()
|
||||
{
|
||||
m_onLNCVReadResponse(true, lncvReadResponse.lncv(), lncvReadResponse.value());
|
||||
});
|
||||
m_pendingLNCVRead.reset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1012,6 +1059,18 @@ bool Kernel::immPacket(std::span<const uint8_t> dccPacket, uint8_t repeat)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Kernel::readLNCV(uint16_t moduleId, uint16_t address, uint16_t lncv, std::function<void(uint16_t, std::error_code)> callback)
|
||||
{
|
||||
assert(isEventLoopThread());
|
||||
|
||||
m_ioContext.post(
|
||||
[this, moduleId, address, lncv, callback]()
|
||||
{
|
||||
m_lncvReads.emplace(LNCVRead{moduleId, address, lncv, std::move(callback)});
|
||||
send(Uhlenbrock::LNCVRead(moduleId, address, lncv), LocoNet::Kernel::LowPriority);
|
||||
});
|
||||
}
|
||||
|
||||
void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
|
||||
{
|
||||
assert(isEventLoopThread());
|
||||
@ -1439,9 +1498,18 @@ void Kernel::sendNextMessage()
|
||||
m_waitingForEchoTimer.async_wait(std::bind(&Kernel::waitingForEchoTimerExpired, this, std::placeholders::_1));
|
||||
|
||||
m_waitingForResponse = hasResponse(message);
|
||||
m_waitingForLNCVReadResponse = m_waitingForResponse && Uhlenbrock::LNCVRead::check(message);
|
||||
if(m_waitingForResponse)
|
||||
{
|
||||
m_waitingForResponseTimer.expires_after(boost::asio::chrono::milliseconds(m_config.responseTimeout));
|
||||
if(m_waitingForLNCVReadResponse)
|
||||
{
|
||||
const auto& lncvRead = static_cast<const Uhlenbrock::LNCVRead&>(message);
|
||||
m_pendingLNCVRead.moduleId = lncvRead.moduleId();
|
||||
m_pendingLNCVRead.address = lncvRead.address();
|
||||
m_pendingLNCVRead.lncv = lncvRead.lncv();
|
||||
}
|
||||
const auto timeout = m_waitingForLNCVReadResponse ? Config::lncvReadResponseTimeout : m_config.responseTimeout;
|
||||
m_waitingForResponseTimer.expires_after(boost::asio::chrono::milliseconds(timeout));
|
||||
m_waitingForResponseTimer.async_wait(std::bind(&Kernel::waitingForResponseTimerExpired, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
@ -1473,7 +1541,28 @@ void Kernel::waitingForResponseTimerExpired(const boost::system::error_code& ec)
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
if(m_lncvActive && Uhlenbrock::LNCVStart::check(lastSentMessage()))
|
||||
if(m_waitingForLNCVReadResponse)
|
||||
{
|
||||
if(!m_lncvReads.empty() &&
|
||||
m_lncvReads.front().moduleId == m_pendingLNCVRead.moduleId &&
|
||||
m_lncvReads.front().address == m_pendingLNCVRead.address &&
|
||||
m_lncvReads.front().lncv == m_pendingLNCVRead.lncv)
|
||||
{
|
||||
m_pendingLNCVRead.reset();
|
||||
EventLoop::call(
|
||||
[callback=std::move(m_lncvReads.front().callback)]()
|
||||
{
|
||||
callback(0, LNCV::Error::noResponse());
|
||||
});
|
||||
m_lncvReads.pop();
|
||||
}
|
||||
|
||||
assert(Uhlenbrock::LNCVRead::check(lastSentMessage()));
|
||||
m_sendQueue[m_sentMessagePriority].pop();
|
||||
m_waitingForResponse = false;
|
||||
sendNextMessage();
|
||||
}
|
||||
else if(m_lncvActive && Uhlenbrock::LNCVStart::check(lastSentMessage()))
|
||||
{
|
||||
EventLoop::call(
|
||||
[this, lncvStart=static_cast<const Uhlenbrock::LNCVStart&>(lastSentMessage())]()
|
||||
@ -1484,6 +1573,9 @@ void Kernel::waitingForResponseTimerExpired(const boost::system::error_code& ec)
|
||||
m_onLNCVReadResponse(false, lncvStart.address(), 0);
|
||||
});
|
||||
|
||||
assert(Uhlenbrock::LNCVStart::check(lastSentMessage()));
|
||||
m_sendQueue[m_sentMessagePriority].pop();
|
||||
m_waitingForResponse = false;
|
||||
sendNextMessage();
|
||||
}
|
||||
else
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
* Copyright (C) 2019-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -27,6 +27,7 @@
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <queue>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/signals2/connection.hpp>
|
||||
#include <span>
|
||||
@ -149,6 +150,20 @@ class Kernel : public ::KernelBase
|
||||
bool m_waitingForEcho;
|
||||
boost::asio::steady_timer m_waitingForEchoTimer;
|
||||
bool m_waitingForResponse;
|
||||
bool m_waitingForLNCVReadResponse = false;
|
||||
struct
|
||||
{
|
||||
uint16_t moduleId = 0;
|
||||
uint16_t address = 0;
|
||||
uint16_t lncv = 0;
|
||||
|
||||
void reset()
|
||||
{
|
||||
moduleId = 0;
|
||||
address = 0;
|
||||
lncv = 0;
|
||||
}
|
||||
} m_pendingLNCVRead;
|
||||
boost::asio::steady_timer m_waitingForResponseTimer;
|
||||
|
||||
TriState m_globalPower;
|
||||
@ -166,6 +181,14 @@ class Kernel : public ::KernelBase
|
||||
uint16_t m_lncvModuleId = 0;
|
||||
uint16_t m_lncvModuleAddress = 0;
|
||||
OnLNCVReadResponse m_onLNCVReadResponse;
|
||||
struct LNCVRead
|
||||
{
|
||||
uint16_t moduleId;
|
||||
uint16_t address;
|
||||
uint16_t lncv;
|
||||
std::function<void(uint16_t, std::error_code)> callback;
|
||||
};
|
||||
std::queue<LNCVRead> m_lncvReads;
|
||||
|
||||
DecoderController* m_decoderController;
|
||||
std::unordered_map<uint16_t, uint8_t> m_addressToSlot;
|
||||
@ -406,6 +429,8 @@ class Kernel : public ::KernelBase
|
||||
return immPacket(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(&dccPacket), sizeof(T)), repeat);
|
||||
}
|
||||
|
||||
void readLNCV(uint16_t moduleId, uint16_t address, uint16_t lncv, std::function<void(uint16_t, std::error_code)> callback);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
|
||||
100
server/src/hardware/protocol/loconet/lncv/lncverror.cpp
Normale Datei
100
server/src/hardware/protocol/loconet/lncv/lncverror.cpp
Normale Datei
@ -0,0 +1,100 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 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 "lncverror.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
class Category final : public std::error_category
|
||||
{
|
||||
public:
|
||||
const char* name() const noexcept final
|
||||
{
|
||||
return "loconet.lncv";
|
||||
}
|
||||
|
||||
std::string message(int ev) const final
|
||||
{
|
||||
switch (static_cast<LocoNet::LNCV::Errc>(ev))
|
||||
{
|
||||
using enum LocoNet::LNCV::Errc;
|
||||
|
||||
case UnexpectedResponse:
|
||||
return "Unexpected response to LNCV request";
|
||||
|
||||
case NoResponse:
|
||||
return "No response to LNCV request";
|
||||
|
||||
case Rejected:
|
||||
return "LNCV request rejected";
|
||||
|
||||
case NotFound:
|
||||
return "LNCV not found";
|
||||
|
||||
case ReadOnly:
|
||||
return "LNCV is read only";
|
||||
|
||||
default:
|
||||
return "Unknown LNCV error";
|
||||
}
|
||||
}
|
||||
|
||||
std::error_condition default_error_condition(int ev) const noexcept final
|
||||
{
|
||||
switch (static_cast<LocoNet::LNCV::Errc>(ev))
|
||||
{
|
||||
using enum LocoNet::LNCV::Errc;
|
||||
|
||||
case NoResponse:
|
||||
return std::errc::timed_out;
|
||||
|
||||
case Rejected:
|
||||
return std::errc::operation_not_permitted;
|
||||
|
||||
case NotFound:
|
||||
return std::errc::no_such_file_or_directory;
|
||||
|
||||
case ReadOnly:
|
||||
return std::errc::read_only_file_system;
|
||||
|
||||
default:
|
||||
return std::error_condition(ev, *this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Category category{};
|
||||
|
||||
}
|
||||
|
||||
namespace LocoNet::LNCV {
|
||||
|
||||
const std::error_category& errorCategory()
|
||||
{
|
||||
return category;
|
||||
}
|
||||
|
||||
std::error_code makeErrorCode(Errc ec)
|
||||
{
|
||||
return {static_cast<int>(ec), errorCategory()};
|
||||
}
|
||||
|
||||
}
|
||||
74
server/src/hardware/protocol/loconet/lncv/lncverror.hpp
Normale Datei
74
server/src/hardware/protocol/loconet/lncv/lncverror.hpp
Normale Datei
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LNCV_LNCVERROR_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LNCV_LNCVERROR_HPP
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace LocoNet::LNCV {
|
||||
|
||||
enum class Errc
|
||||
{
|
||||
// zero means no error
|
||||
UnexpectedResponse = 1,
|
||||
NoResponse = 2,
|
||||
Rejected = 3,
|
||||
NotFound = 4,
|
||||
ReadOnly = 5,
|
||||
};
|
||||
|
||||
std::error_code makeErrorCode(Errc ec);
|
||||
|
||||
namespace Error
|
||||
{
|
||||
inline std::error_code unexpectedResponse()
|
||||
{
|
||||
return makeErrorCode(Errc::UnexpectedResponse);
|
||||
}
|
||||
|
||||
inline std::error_code noResponse()
|
||||
{
|
||||
return makeErrorCode(Errc::NoResponse);
|
||||
}
|
||||
|
||||
inline std::error_code rejected()
|
||||
{
|
||||
return makeErrorCode(Errc::Rejected);
|
||||
}
|
||||
|
||||
inline std::error_code notFound()
|
||||
{
|
||||
return makeErrorCode(Errc::NotFound);
|
||||
}
|
||||
|
||||
inline std::error_code readOnly()
|
||||
{
|
||||
return makeErrorCode(Errc::ReadOnly);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::is_error_code_enum<LocoNet::LNCV::Errc> : std::true_type {};
|
||||
|
||||
#endif
|
||||
65
server/src/hardware/protocol/loconet/lncv/lncvutils.hpp
Normale Datei
65
server/src/hardware/protocol/loconet/lncv/lncvutils.hpp
Normale Datei
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LNCV_LNCVUTILS_HPP
|
||||
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LNCV_LNCVUTILS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include "lncverror.hpp"
|
||||
|
||||
namespace LocoNet::LNCV {
|
||||
|
||||
/**
|
||||
* @brief Interpret OPC_LONG_ACK status byte for LNCV operations.
|
||||
*
|
||||
* @note The status values used here are based on monitoring LocoNet traffic
|
||||
* generated by a Uhlenbrock Intellibox performing LNCV operations against
|
||||
* Uhlenbrock devices.
|
||||
*
|
||||
* @note LNCV was introduced by Uhlenbrock, but no public specification for
|
||||
* OPC_LONG_ACK status values have been found. These mappings therefore
|
||||
* reflect observed, de-facto behavior and may vary for non-Uhlenbrock
|
||||
* devices.
|
||||
*/
|
||||
[[nodiscard]] inline std::error_code parseLongAck(uint8_t status) noexcept
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case 0x00:
|
||||
return Error::rejected();
|
||||
|
||||
case 0x01:
|
||||
return Error::notFound();
|
||||
|
||||
case 0x03:
|
||||
return Error::readOnly();
|
||||
|
||||
case 0x7F:
|
||||
return {}; // no error
|
||||
|
||||
default:
|
||||
return Error::unexpectedResponse();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -33,12 +33,14 @@ namespace Category
|
||||
constexpr std::string_view colorsAndAlignment = "category:colors_and_alignment";
|
||||
constexpr std::string_view debug = "category:debug";
|
||||
constexpr std::string_view developer = "category:developer";
|
||||
constexpr std::string_view driver = "category:driver";
|
||||
constexpr std::string_view general = "category:general";
|
||||
constexpr std::string_view info = "category:info";
|
||||
constexpr std::string_view input = "category:input";
|
||||
constexpr std::string_view log = "category:log";
|
||||
constexpr std::string_view network = "category:network";
|
||||
constexpr std::string_view options = "category:options";
|
||||
constexpr std::string_view status = "category:status";
|
||||
constexpr std::string_view trains = "category:trains";
|
||||
constexpr std::string_view zones = "category:zones";
|
||||
}
|
||||
|
||||
37
server/src/utils/unit.hpp
Normale Datei
37
server/src/utils/unit.hpp
Normale Datei
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* This file is part of Traintastic,
|
||||
* see <https://github.com/traintastic/traintastic>.
|
||||
*
|
||||
* Copyright (C) 2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef TRAINTASTIC_SERVER_UTILS_UNIT_HPP
|
||||
#define TRAINTASTIC_SERVER_UTILS_UNIT_HPP
|
||||
|
||||
#include <string_view>
|
||||
|
||||
struct Unit
|
||||
{
|
||||
static constexpr std::string_view ampere{"A"};
|
||||
static constexpr std::string_view degreeCelcius{"\u00B0C"};
|
||||
static constexpr std::string_view milliSeconds{"ms"};
|
||||
static constexpr std::string_view percent{"%"};
|
||||
static constexpr std::string_view seconds{"s"};
|
||||
static constexpr std::string_view volt{"C"};
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -43,6 +43,8 @@
|
||||
#include "../core/abstractvectorproperty.hpp"
|
||||
#include "../core/controllerlist.hpp"
|
||||
|
||||
#include "../hardware/booster/booster.hpp"
|
||||
#include "../hardware/booster/list/boosterlist.hpp"
|
||||
#include "../hardware/input/input.hpp"
|
||||
#include "../hardware/input/monitor/inputmonitor.hpp"
|
||||
#include "../hardware/input/list/inputlist.hpp"
|
||||
@ -123,12 +125,14 @@ void World::init(World& world)
|
||||
world.outputControllers.setValueInternal(std::make_shared<ControllerList<OutputController>>(world, world.outputControllers.name()));
|
||||
world.identificationControllers.setValueInternal(std::make_shared<ControllerList<IdentificationController>>(world, world.identificationControllers.name()));
|
||||
world.lncvProgrammingControllers.setValueInternal(std::make_shared<ControllerList<LNCVProgrammingController>>(world, world.lncvProgrammingControllers.name()));
|
||||
world.loconetInterfaces.setValueInternal(std::make_shared<ControllerList<LocoNetInterface>>(world, world.loconetInterfaces.name()));
|
||||
|
||||
world.interfaces.setValueInternal(std::make_shared<InterfaceList>(world, world.interfaces.name()));
|
||||
world.decoders.setValueInternal(std::make_shared<DecoderList>(world, world.decoders.name(), decoderListColumns));
|
||||
world.inputs.setValueInternal(std::make_shared<InputList>(world, world.inputs.name(), inputListColumns));
|
||||
world.outputs.setValueInternal(std::make_shared<OutputList>(world, world.outputs.name(), outputListColumns));
|
||||
world.identifications.setValueInternal(std::make_shared<IdentificationList>(world, world.outputs.name(), identificationListColumns));
|
||||
world.boosters.setValueInternal(std::make_shared<BoosterList>(world, world.boosters.name()));
|
||||
world.boards.setValueInternal(std::make_shared<BoardList>(world, world.boards.name()));
|
||||
world.zones.setValueInternal(std::make_shared<ZoneList>(world, world.zones.name()));
|
||||
world.clock.setValueInternal(std::make_shared<Clock>(world, world.clock.name()));
|
||||
@ -177,11 +181,13 @@ World::World(Private /*unused*/) :
|
||||
outputControllers{this, "output_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
identificationControllers{this, "identification_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
lncvProgrammingControllers{this, "lncv_programming_controllers", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
loconetInterfaces{this, "loconet_interfaces", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
interfaces{this, "interfaces", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
inputs{this, "inputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
identifications{this, "identifications", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
boosters{this, "boosters", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore},
|
||||
boards{this, "boards", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
zones{this, "zones", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::NoStore | PropertyFlags::ScriptReadOnly},
|
||||
clock{this, "clock", nullptr, PropertyFlags::ReadOnly | PropertyFlags::SubObject | PropertyFlags::Store | PropertyFlags::ScriptReadOnly},
|
||||
@ -381,6 +387,8 @@ World::World(Private /*unused*/) :
|
||||
m_interfaceItems.add(identificationControllers);
|
||||
Attributes::addObjectEditor(lncvProgrammingControllers, false);
|
||||
m_interfaceItems.add(lncvProgrammingControllers);
|
||||
Attributes::addObjectEditor(loconetInterfaces, false);
|
||||
m_interfaceItems.add(loconetInterfaces);
|
||||
|
||||
Attributes::addObjectEditor(interfaces, false);
|
||||
m_interfaceItems.add(interfaces);
|
||||
@ -392,6 +400,8 @@ World::World(Private /*unused*/) :
|
||||
m_interfaceItems.add(outputs);
|
||||
Attributes::addObjectEditor(identifications, false);
|
||||
m_interfaceItems.add(identifications);
|
||||
Attributes::addObjectEditor(boosters, false);
|
||||
m_interfaceItems.add(boosters);
|
||||
Attributes::addObjectEditor(throttles, false);
|
||||
m_interfaceItems.add(throttles);
|
||||
Attributes::addObjectEditor(boards, false);
|
||||
|
||||
@ -44,11 +44,13 @@ class InputController;
|
||||
class OutputController;
|
||||
class IdentificationController;
|
||||
class LNCVProgrammingController;
|
||||
class LocoNetInterface;
|
||||
class InterfaceList;
|
||||
class DecoderList;
|
||||
class InputList;
|
||||
class OutputList;
|
||||
class IdentificationList;
|
||||
class BoosterList;
|
||||
class BoardList;
|
||||
class ZoneList;
|
||||
class BlockRailTileList;
|
||||
@ -121,12 +123,14 @@ class World : public Object
|
||||
ObjectProperty<ControllerList<OutputController>> outputControllers;
|
||||
ObjectProperty<ControllerList<IdentificationController>> identificationControllers;
|
||||
ObjectProperty<ControllerList<LNCVProgrammingController>> lncvProgrammingControllers;
|
||||
ObjectProperty<ControllerList<LocoNetInterface>> loconetInterfaces;
|
||||
|
||||
ObjectProperty<InterfaceList> interfaces;
|
||||
ObjectProperty<DecoderList> decoders;
|
||||
ObjectProperty<InputList> inputs;
|
||||
ObjectProperty<OutputList> outputs;
|
||||
ObjectProperty<IdentificationList> identifications;
|
||||
ObjectProperty<BoosterList> boosters;
|
||||
ObjectProperty<BoardList> boards;
|
||||
ObjectProperty<ZoneList> zones;
|
||||
ObjectProperty<Clock> clock;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2019-2024 Reinder Feenstra
|
||||
* Copyright (C) 2019-2025 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -40,6 +40,7 @@
|
||||
#include "../hardware/decoder/decoder.hpp"
|
||||
#include "../hardware/decoder/decoderfunction.hpp"
|
||||
#include "../hardware/identification/identification.hpp"
|
||||
#include "../hardware/booster/booster.hpp"
|
||||
#include "../vehicle/rail/railvehicles.hpp"
|
||||
#include "../vehicle/rail/freightwagon.hpp" //! \todo Remove in v0.4
|
||||
#include "../train/train.hpp"
|
||||
@ -364,6 +365,10 @@ void WorldLoader::createObject(ObjectData& objectData)
|
||||
}
|
||||
else if(classId == Identification::classId)
|
||||
objectData.object = Identification::create(*m_world, id);
|
||||
else if(classId == Booster::classId)
|
||||
{
|
||||
objectData.object = Booster::create(*m_world, id);
|
||||
}
|
||||
else if(classId == Board::classId)
|
||||
objectData.object = Board::create(*m_world, id);
|
||||
else if(startsWith(classId, Tiles::classIdPrefix))
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
*
|
||||
* This file is part of the traintastic source code.
|
||||
*
|
||||
* Copyright (C) 2021-2024 Reinder Feenstra
|
||||
* Copyright (C) 2021-2026 Reinder Feenstra
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -100,6 +100,7 @@ enum class LogMessage : uint32_t
|
||||
I2003_FIRMWARE_VERSION_X = LogMessageOffset::info + 2003,
|
||||
I2004_HSI_88_X = LogMessageOffset::info + 2004,
|
||||
I2005_X = LogMessageOffset::info + 2005,
|
||||
I2006_BOOSTER_X_SOFTWARE_VERSION_X = LogMessageOffset::info + 2006,
|
||||
I3001_THROTTLE_X_ACQUIRED_TRAIN_X = LogMessageOffset::info + 3001,
|
||||
I3002_THROTTLE_X_RELEASED_TRAIN_X = LogMessageOffset::info + 3002,
|
||||
I9001_STOPPED_SCRIPT = LogMessageOffset::info + 9001,
|
||||
@ -167,6 +168,9 @@ enum class LogMessage : uint32_t
|
||||
W2018_TIMEOUT_NO_ECHO_WITHIN_X_MS = LogMessageOffset::warning + 2018,
|
||||
W2019_Z21_BROADCAST_FLAG_MISMATCH = LogMessageOffset::warning + 2019,
|
||||
W2020_DCCEXT_RCN213_IS_NOT_SUPPORTED = LogMessageOffset::warning + 2020,
|
||||
// RESERVED: W2025_READING_BOOSTER_X_SOFTWARE_VERSION_FAILED_X = LogMessageOffset::warning + 2025,
|
||||
W2026_READING_BOOSTER_X_TEMPERATURE_FAILED_X = LogMessageOffset::warning + 2026,
|
||||
W2027_READING_BOOSTER_X_LOAD_FAILED_X = LogMessageOffset::warning + 2027,
|
||||
W3001_NX_BUTTON_CONNECTED_TO_TWO_BLOCKS = LogMessageOffset::warning + 3001,
|
||||
W3002_NX_BUTTON_NOT_CONNECTED_TO_ANY_BLOCK = LogMessageOffset::warning + 3002,
|
||||
W3003_LOCKED_TURNOUT_CHANGED = LogMessageOffset::warning + 3003,
|
||||
@ -207,6 +211,9 @@ enum class LogMessage : uint32_t
|
||||
E2022_SOCKET_CREATE_FAILED_X = LogMessageOffset::error + 2022,
|
||||
E2023_SOCKET_IOCTL_FAILED_X = LogMessageOffset::error + 2023,
|
||||
E2024_UNKNOWN_LOCOMOTIVE_MFX_UID_X = LogMessageOffset::error + 2024,
|
||||
E2025_READING_BOOSTER_X_SOFTWARE_VERSION_FAILED_X = LogMessageOffset::error + 2025,
|
||||
E2026_READING_BOOSTER_X_TEMPERATURE_FAILED_X = LogMessageOffset::error + 2026,
|
||||
E2027_READING_BOOSTER_X_LOAD_FAILED_X = LogMessageOffset::error + 2027,
|
||||
E3001_CANT_DELETE_RAIL_VEHICLE_WHEN_IN_ACTIVE_TRAIN = LogMessageOffset::error + 3001,
|
||||
E3002_CANT_DELETE_ACTIVE_TRAIN = LogMessageOffset::error + 3002,
|
||||
E3003_TRAIN_STOPPED_ON_TURNOUT_X_CHANGED = LogMessageOffset::error + 3003,
|
||||
|
||||
@ -3306,5 +3306,65 @@
|
||||
{
|
||||
"term": "zone:speed_limit",
|
||||
"definition": "Speed limit"
|
||||
},
|
||||
{
|
||||
"term": "hardware:boosters",
|
||||
"definition": "Boosters"
|
||||
},
|
||||
{
|
||||
"term": "booster:type",
|
||||
"definition": "Type"
|
||||
},
|
||||
{
|
||||
"term": "booster:load",
|
||||
"definition": "Load"
|
||||
},
|
||||
{
|
||||
"term": "booster:temperature",
|
||||
"definition": "Temperature"
|
||||
},
|
||||
{
|
||||
"term": "booster:current",
|
||||
"definition": "Current"
|
||||
},
|
||||
{
|
||||
"term": "booster:voltage",
|
||||
"definition": "Voltage"
|
||||
},
|
||||
{
|
||||
"term": "booster:input_voltage",
|
||||
"definition": "Input voltage"
|
||||
},
|
||||
{
|
||||
"term": "booster_driver:poll_interval",
|
||||
"definition": "Poll interval"
|
||||
},
|
||||
{
|
||||
"term": "category:status",
|
||||
"definition": "Status"
|
||||
},
|
||||
{
|
||||
"term": "message:I2006",
|
||||
"definition": "Booster '%1' software version: %2"
|
||||
},
|
||||
{
|
||||
"term": "message:W2026",
|
||||
"definition": "Reading booster '%1' temperature failed: %2"
|
||||
},
|
||||
{
|
||||
"term": "message:W2027",
|
||||
"definition": "Reading booster '%1' load failed: %2"
|
||||
},
|
||||
{
|
||||
"term": "message:E2025",
|
||||
"definition": "Reading booster '%1' software version failed: %2"
|
||||
},
|
||||
{
|
||||
"term": "message:E2026",
|
||||
"definition": "Reading booster '%1' temperature failed: %2"
|
||||
},
|
||||
{
|
||||
"term": "message:E2027",
|
||||
"definition": "Reading booster '%1' load failed: %2"
|
||||
}
|
||||
]
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren