Merge remote-tracking branch 'origin/master' into 11-märklin-cs2cs3-hardware-support

Dieser Commit ist enthalten in:
Reinder Feenstra 2023-07-09 23:15:32 +02:00
Commit 54ba3664a6
49 geänderte Dateien mit 1116 neuen und 3260 gelöschten Zeilen

Datei anzeigen

@ -99,15 +99,16 @@ int main(int argc, char* argv[])
const bool logMissingStrings = !DeveloperSettings::instance().logMissingStringsDir.value().isEmpty();
Locale* fallback = nullptr;
const auto localePath = getLocalePath();
Locale* fallback = new Locale(localePath / "neutral.lang");
if(language != languageDefault && !DeveloperSettings::instance().dontLoadFallbackLanguage)
{
fallback = new Locale(getLocalePath() / languageDefault.toStdString().append(".txt"));
fallback = new Locale(localePath / languageDefault.toStdString().append(".lang"), fallback);
if(logMissingStrings)
fallback->enableMissingLogging();
}
Locale::instance = new Locale(getLocalePath() / language.toStdString().append(".lang"), fallback);
Locale::instance = new Locale(localePath / language.toStdString().append(".lang"), fallback);
if(logMissingStrings)
const_cast<Locale*>(Locale::instance)->enableMissingLogging();

Datei anzeigen

@ -24,19 +24,20 @@
#include <QComboBox>
#include <QDir>
#include <QDirIterator>
#include <QMessageBox>
#include <traintastic/locale/locale.hpp>
#include "generalsettings.hpp"
#include <traintastic/utils/standardpaths.hpp>
static QString getLanguageName(const QString& filename)
{
QFile file(filename);
if(file.open(QIODevice::ReadOnly | QIODevice::Text))
try
{
Locale locale(getLocalePath() / filename.toStdString());
return locale.translate(QString("language:").append(QFileInfo(filename).baseName()));
}
catch(...)
{
const QString header = QStringLiteral("## Traintastic language file: ");
QString line = QString::fromUtf8(file.readLine());
if(line.startsWith(header))
return line.remove(0, header.length()).trimmed();
}
return "";
}
@ -53,6 +54,8 @@ GeneralSettingsWidget::GeneralSettingsWidget(QWidget* parent)
while(it.hasNext())
{
const QString filename = it.next();
if(QFileInfo(filename).baseName() == "neutral")
continue;
const QString label = getLanguageName(filename);
if(!label.isEmpty())
{
@ -62,9 +65,10 @@ GeneralSettingsWidget::GeneralSettingsWidget(QWidget* parent)
}
}
connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[cb](int index)
[this, cb](int index)
{
GeneralSettings::instance().language.setValue(cb->itemData(index).toString());
QMessageBox::information(this, Locale::tr("qtapp.settings:restart_required"), Locale::tr("qtapp.settings.general:language_changed_restart_required"));
});
add(s.language.name(), cb);
}

Datei anzeigen

@ -103,6 +103,12 @@ struct Attributes
property.addAttribute(AttributeName::Max, max);
}
template<typename T>
static inline void addMinMax(Property<T>& property, std::pair<T, T> range)
{
addMinMax(property, range.first, range.second);
}
template<class T, class Unit>
static inline void addMinMax(UnitProperty<T, Unit>& property, T min, T max, Unit unit)
{
@ -147,6 +153,12 @@ struct Attributes
property.setAttribute(AttributeName::Max, max);
}
template<typename T>
static inline void setMinMax(Property<T>& property, std::pair<T, T> range)
{
setMinMax(property, range.first, range.second);
}
template<class T, class Unit>
static inline void setMinMax(UnitProperty<T, Unit>& property, T min, T max, Unit unit)
{

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2022 Reinder Feenstra
* Copyright (C) 2019-2020,2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -124,6 +124,31 @@ class Property : public AbstractProperty
}
}
if constexpr(std::is_enum_v<T> && !is_set_v<T>)
{
if(auto it = m_attributes.find(AttributeName::Values); it != m_attributes.end())
{
if(auto* span = dynamic_cast<SpanAttribute<T>*>(it->second.get()))
{
const auto values = span->values();
if(std::find(values.begin(), values.end(), value) == values.end())
throw out_of_range_error();
}
else if(auto* vectorRef = dynamic_cast<VectorRefAttribute<T>*>(it->second.get()))
{
const auto* values = vectorRef->values();
if(!values || std::find(values->begin(), values->end(), value) == values->end())
throw out_of_range_error();
}
else if(auto* vector = dynamic_cast<VectorAttribute<T>*>(it->second.get()))
{
const auto& values = vector->values();
if(std::find(values.begin(), values.end(), value) == values.end())
throw out_of_range_error();
}
}
}
if(!m_onSet || m_onSet(value))
{
m_value = value;

Datei anzeigen

@ -70,6 +70,11 @@ class SpanAttribute : public AbstractValuesAttribute
return to<std::string>(m_values[index]);
}
tcb::span<const T> values() const
{
return m_values;
}
void setValues(tcb::span<const T> values)
{
if(m_values.size() != values.size() || std::memcmp(m_values.data(), values.data(), m_values.size()) != 0)

Datei anzeigen

@ -70,6 +70,11 @@ class VectorRefAttribute : public AbstractValuesAttribute
return to<std::string>((*m_values)[index]);
}
const std::vector<T>* values() const
{
return m_values;
}
void setValues(const std::vector<T>* values)
{
if(m_values != values)

Datei anzeigen

@ -1,38 +0,0 @@
/**
* server/src/enum/decoderprotocol.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020,2023 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_DECODERPROTOCOL_HPP
#define TRAINTASTIC_SERVER_ENUM_DECODERPROTOCOL_HPP
#include <traintastic/enum/decoderprotocol.hpp>
#include <array>
inline constexpr std::array<DecoderProtocol, 6> decoderProtocolValues{{
DecoderProtocol::Auto,
DecoderProtocol::DCC,
DecoderProtocol::MFX,
DecoderProtocol::Motorola,
DecoderProtocol::Selectrix,
DecoderProtocol::Custom,
}};
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -43,7 +43,21 @@ const std::shared_ptr<Decoder> Decoder::null;
Decoder::Decoder(World& world, std::string_view _id) :
IdObject(world, _id),
name{this, "name", "", PropertyFlags::ReadWrite | PropertyFlags::Store},
interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store, nullptr,
interface{this, "interface", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<DecoderController>& value)
{
if(value)
{
assert(!value->decoderProtocols().empty());
Attributes::setValues(protocol, value->decoderProtocols());
if(!checkProtocol())
protocolChanged();
Attributes::setVisible(protocol, true);
}
else
Attributes::setVisible(protocol, false);
updateEditable();
},
[this](const std::shared_ptr<DecoderController>& newValue)
{
if(!newValue || newValue->addDecoder(*this))
@ -54,24 +68,13 @@ Decoder::Decoder(World& world, std::string_view _id) :
}
return false;
}},
protocol{this, "protocol", DecoderProtocol::Auto, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const DecoderProtocol& value)
protocol{this, "protocol", DecoderProtocol::None, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const DecoderProtocol& /*value*/)
{
if(value == DecoderProtocol::DCC && DCC::isLongAddress(address))
longAddress = true;
protocolChanged();
updateEditable();
}},
address{this, "address", 0, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const uint16_t& value)
{
if(protocol == DecoderProtocol::DCC)
{
if(DCC::isLongAddress(value))
longAddress = true;
updateEditable();
}
}},
longAddress{this, "long_address", false, PropertyFlags::ReadWrite | PropertyFlags::Store},
address{this, "address", 0, PropertyFlags::ReadWrite | PropertyFlags::Store},
emergencyStop{this, "emergency_stop", false, PropertyFlags::ReadWrite,
[this](const bool& /*value*/)
{
@ -121,15 +124,16 @@ Decoder::Decoder(World& world, std::string_view _id) :
m_interfaceItems.add(interface);
Attributes::addEnabled(protocol, false);
Attributes::addValues(protocol, decoderProtocolValues);
Attributes::addValues(protocol, tcb::span<const DecoderProtocol>{});
Attributes::addVisible(protocol, false);
m_interfaceItems.add(protocol);
Attributes::addDisplayName(address, DisplayName::Hardware::address);
Attributes::addEnabled(address, false);
Attributes::addMinMax(address, std::pair<uint16_t, uint16_t>(0, 0));
Attributes::addVisible(address, false);
m_interfaceItems.add(address);
Attributes::addEnabled(longAddress, false);
m_interfaceItems.add(longAddress);
Attributes::addObjectEditor(emergencyStop, false);
m_interfaceItems.add(emergencyStop);
@ -142,6 +146,8 @@ Decoder::Decoder(World& world, std::string_view _id) :
Attributes::addDisplayName(speedSteps, DisplayName::Hardware::speedSteps);
Attributes::addEnabled(speedSteps, false);
Attributes::addValues(speedSteps, tcb::span<const uint8_t>{});
Attributes::addVisible(speedSteps, false);
m_interfaceItems.add(speedSteps);
Attributes::addMinMax(throttle, throttleMin, throttleMax);
@ -166,6 +172,11 @@ void Decoder::loaded()
IdObject::loaded();
if(interface)
{
Attributes::setValues(protocol, interface->decoderProtocols());
Attributes::setVisible(protocol, true);
checkProtocol(); //! \todo log something if protocol is changed??
protocolChanged();
if(!interface->addDecoder(*this))
{
if(auto object = std::dynamic_pointer_cast<Object>(interface.value()))
@ -329,6 +340,75 @@ void Decoder::worldEvent(WorldState state, WorldEvent event)
}
}
void Decoder::protocolChanged()
{
if(interface)
{
// address:
const auto addressRange = interface->decoderAddressMinMax(protocol);
const bool hasAddress = addressRange.first <= addressRange.second;
Attributes::setVisible(address, hasAddress);
if(hasAddress)
{
Attributes::setMinMax(address, addressRange);
checkAddress();
}
else
Attributes::setMinMax(address, std::pair<uint16_t, uint16_t>(0, 0));
// speed steps:
const auto values = interface->decoderSpeedSteps(protocol);
Attributes::setVisible(speedSteps, !values.empty());
if(!values.empty())
{
Attributes::setValues(speedSteps, values);
checkSpeedSteps();
}
else
Attributes::setValues(speedSteps, tcb::span<const uint8_t>{});
}
else
{
Attributes::setVisible(address, false);
Attributes::setVisible(speedSteps, false);
}
}
bool Decoder::checkProtocol()
{
const auto protocols = protocol.getSpanAttribute<DecoderProtocol>(AttributeName::Values).values();
assert(!protocols.empty());
if(auto it = std::find(protocols.begin(), protocols.end(), protocol); it == protocols.end())
{
protocol = protocols.front();
return true;
}
return false;
}
bool Decoder::checkAddress()
{
const auto addressMin = address.getAttribute<uint16_t>(AttributeName::Min);
const auto addressMax = address.getAttribute<uint16_t>(AttributeName::Max);
if(!inRange(address.value(), addressMin, addressMax))
{
address = std::clamp(address.value(), addressMin, addressMax);
return true;
}
return false;
}
bool Decoder::checkSpeedSteps()
{
const auto values = speedSteps.getSpanAttribute<uint8_t>(AttributeName::Values).values();
if(auto it = std::find(values.begin(), values.end(), speedSteps); it == values.end())
{
speedSteps = values.back();
return true;
}
return false;
}
void Decoder::updateEditable()
{
updateEditable(contains(m_world.state.value(), WorldState::Edit));
@ -339,10 +419,9 @@ void Decoder::updateEditable(bool editable)
const bool stopped = editable && almostZero(throttle.value());
Attributes::setEnabled(name, editable);
Attributes::setEnabled(interface, stopped);
Attributes::setEnabled(protocol, stopped);
Attributes::setEnabled(protocol, stopped && protocol.getSpanAttribute<DecoderProtocol>(AttributeName::Values).length() > 1);
Attributes::setEnabled(address, stopped);
Attributes::setEnabled(longAddress, stopped && protocol == DecoderProtocol::DCC && !DCC::isLongAddress(address));
Attributes::setEnabled(speedSteps, stopped);
Attributes::setEnabled(speedSteps, stopped && speedSteps.getSpanAttribute<uint8_t>(AttributeName::Values).length() > 1);
}
void Decoder::changed(DecoderChangeFlags changes, uint32_t functionNumber)

Datei anzeigen

@ -26,7 +26,7 @@
#include <type_traits>
#include "../../core/idobject.hpp"
#include "../../core/objectproperty.hpp"
#include "../../enum/decoderprotocol.hpp"
#include <traintastic/enum/decoderprotocol.hpp>
#include "../../enum/direction.hpp"
#include "decodercontroller.hpp"
#include "decoderfunctions.hpp"
@ -52,6 +52,24 @@ class Decoder : public IdObject
void loaded() final;
void destroying() override;
void worldEvent(WorldState state, WorldEvent event) final;
void protocolChanged();
//! \brief Check and correct protocol
//! If the current value isn't in the list of valid protocols the protocol is set the the first valid one.
//! \return \c true if adjusted, \c false if unchanged
bool checkProtocol();
//! \brief Check and correct address
//! If the current value isn't within the protocol address range, the value is set to the nearest valid one.
//! \return \c true if adjusted, \c false if unchanged
bool checkAddress();
//! \brief Check and correct speed steps
//! If the current value isn't a valid speed step value, the value is set to the highest valid one.
//! \return \c true if adjusted, \c false if unchanged
bool checkSpeedSteps();
void updateEditable();
void updateEditable(bool editable);
void changed(DecoderChangeFlags changes, uint32_t functionNumber = 0);
@ -86,7 +104,6 @@ class Decoder : public IdObject
ObjectProperty<DecoderController> interface;
Property<DecoderProtocol> protocol;
Property<uint16_t> address;
Property<bool> longAddress;
Property<bool> emergencyStop;
Property<Direction> direction;
Method<void()> toggleDirection;

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022 Reinder Feenstra
* Copyright (C) 2021-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -25,6 +25,7 @@
#include "decoderchangeflags.hpp"
#include "list/decoderlist.hpp"
#include "list/decoderlisttablemodel.hpp"
#include "../protocol/dcc/dcc.hpp"
#include "../../core/attributes.hpp"
#include "../../core/objectproperty.tpp"
#include "../../core/controllerlist.hpp"
@ -40,11 +41,57 @@ DecoderController::DecoderController(IdObject& interface, DecoderListColumn colu
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
}
std::pair<uint16_t, uint16_t> DecoderController::decoderAddressMinMax(DecoderProtocol protocol) const
{
switch(protocol)
{
case DecoderProtocol::DCCShort:
return {DCC::addressMin, DCC::addressShortMax};
case DecoderProtocol::DCCLong:
return {DCC::addressMin, DCC::addressLongMax};
case DecoderProtocol::Motorola:
return {1, 255};
case DecoderProtocol::Selectrix:
return {1, 112};
case DecoderProtocol::None:
return noAddressMinMax;
}
assert(false);
return noAddressMinMax;
}
tcb::span<const uint8_t> DecoderController::decoderSpeedSteps(DecoderProtocol protocol) const
{
static constexpr std::array<uint8_t, 3> dccSpeedSteps{{14, 28, 128}};
static constexpr std::array<uint8_t, 3> motorolaSpeedSteps{{14, 27, 28}};
static constexpr std::array<uint8_t, 1> selectrixSpeedSteps{{32}};
switch(protocol)
{
case DecoderProtocol::DCCShort:
case DecoderProtocol::DCCLong:
return dccSpeedSteps;
case DecoderProtocol::Motorola:
return motorolaSpeedSteps;
case DecoderProtocol::Selectrix:
return selectrixSpeedSteps;
case DecoderProtocol::None:
return {};
}
assert(false);
return {};
}
bool DecoderController::addDecoder(Decoder& decoder)
{
if(decoder.protocol != DecoderProtocol::Auto && findDecoder(decoder) != m_decoders.end())
return false;
if(findDecoder(DecoderProtocol::Auto, decoder.address) != m_decoders.end())
if(findDecoder(decoder) != m_decoders.end())
return false;
m_decoders.emplace_back(decoder.shared_ptr<Decoder>());
@ -64,13 +111,11 @@ bool DecoderController::removeDecoder(Decoder& decoder)
return false;
}
const std::shared_ptr<Decoder>& DecoderController::getDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress, bool fallbackToProtocolAuto)
const std::shared_ptr<Decoder>& DecoderController::getDecoder(DecoderProtocol protocol, uint16_t address)
{
auto it = findDecoder(protocol, address, dccLongAddress);
auto it = findDecoder(protocol, address);
if(it != m_decoders.end())
return *it;
if(fallbackToProtocolAuto && protocol != DecoderProtocol::Auto && (it = findDecoder(DecoderProtocol::Auto, address)) != m_decoders.end())
return *it;
return Decoder::null;
}
@ -96,20 +141,11 @@ void DecoderController::destroying()
DecoderController::DecoderVector::iterator DecoderController::findDecoder(const Decoder& decoder)
{
return findDecoder(decoder.protocol, decoder.address, decoder.longAddress);
return findDecoder(decoder.protocol, decoder.address);
}
DecoderController::DecoderVector::iterator DecoderController::findDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress)
DecoderController::DecoderVector::iterator DecoderController::findDecoder(DecoderProtocol protocol, uint16_t address)
{
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;
});
}
return std::find_if(m_decoders.begin(), m_decoders.end(),
[protocol, address](const auto& it)
{

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021-2022 Reinder Feenstra
* Copyright (C) 2021-2023 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,10 +26,11 @@
#include <cstdint>
#include <vector>
#include <memory>
#include <tcb/span.hpp>
#include "../../core/objectproperty.hpp"
#ifdef interface
#undef interface // interface is defined in combaseapi.h
#undef interface // interface is defined in combaseapi.h
#endif
class IdObject;
@ -56,18 +57,34 @@ class DecoderController
void destroying();
DecoderVector::iterator findDecoder(const Decoder& decoder);
DecoderVector::iterator findDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress = false);
DecoderVector::iterator findDecoder(DecoderProtocol protocol, uint16_t address);
/// \brief restore speed of all decoders that are not (emergency) stopped
void restoreDecoderSpeed();
public:
static constexpr std::pair<uint16_t, uint16_t> noAddressMinMax{std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::min()};
ObjectProperty<DecoderList> decoders;
//! \brief Get supported protocols
//! \return Supported protocols, may not be empty and must be constant for the instance!
virtual tcb::span<const DecoderProtocol> decoderProtocols() const = 0;
//! \brief Get address range for given protocol
//! \param[in] protocol The decoder protocol
//! \return Address range or \c noAddressMinMax if address isn't supported for the given protocol
virtual std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const;
//! \brief Get speed step options for given protocol
//! \param[in] protocol The decoder protocol
//! \return Speed step options for the given protocol
virtual tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const;
[[nodiscard]] bool addDecoder(Decoder& decoder);
[[nodiscard]] bool removeDecoder(Decoder& decoder);
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address, bool dccLongAddress = false, bool fallbackToProtocolAuto = false);
const std::shared_ptr<Decoder>& getDecoder(DecoderProtocol protocol, uint16_t address);
virtual void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) = 0;
};

Datei anzeigen

@ -74,15 +74,14 @@ std::shared_ptr<Decoder> DecoderList::getDecoder(uint16_t address) const
return {};
}
std::shared_ptr<Decoder> DecoderList::getDecoder(DecoderProtocol protocol, uint16_t address, bool longAddress) const
std::shared_ptr<Decoder> DecoderList::getDecoder(DecoderProtocol protocol, uint16_t address) const
{
auto it = std::find_if(begin(), end(),
[protocol, address, longAddress](const auto& decoder)
[protocol, address](const auto& decoder)
{
return
decoder->protocol.value() == protocol &&
decoder->address.value() == address &&
(protocol != DecoderProtocol::DCC || decoder->longAddress == longAddress);
decoder->address.value() == address;
});
if(it != end())
return *it;

Datei anzeigen

@ -46,7 +46,7 @@ class DecoderList : public ObjectList<Decoder>
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;
std::shared_ptr<Decoder> getDecoder(DecoderProtocol protocol, uint16_t address) const;
};
#endif

Datei anzeigen

@ -25,6 +25,7 @@
#include "../decoder/list/decoderlisttablemodel.hpp"
#include "../input/list/inputlist.hpp"
#include "../output/list/outputlist.hpp"
#include "../protocol/dcc/dcc.hpp"
#include "../protocol/dccplusplus/kernel.hpp"
#include "../protocol/dccplusplus/settings.hpp"
#include "../protocol/dccplusplus/messages.hpp"
@ -75,6 +76,52 @@ DCCPlusPlusInterface::DCCPlusPlusInterface(World& world, std::string_view _id)
m_interfaceItems.insertBefore(inputs, notes);
m_interfaceItems.insertBefore(outputs, notes);
m_dccplusplusPropertyChanged = dccplusplus->propertyChanged.connect(
[this](BaseProperty& property)
{
if(m_kernel && &property != &dccplusplus->startupDelay)
m_kernel->setConfig(dccplusplus->config());
if(&property == &dccplusplus->speedSteps)
{
// update speedsteps of all decoders, DCC++ only has a global speedsteps setting
const auto values = decoderSpeedSteps(DecoderProtocol::DCCShort); // identical for DCCLong
assert(values.size() == 1);
for(const auto& decoder : *decoders)
{
Attributes::setValues(decoder->speedSteps, values);
decoder->speedSteps.setValueInternal(values.front());
}
}
});
updateEnabled();
}
tcb::span<const DecoderProtocol> DCCPlusPlusInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> DCCPlusPlusInterface::decoderAddressMinMax(DecoderProtocol protocol) const
{
if(protocol == DecoderProtocol::DCCLong)
return {DCC::addressLongStart, DCC::addressLongMax}; // DCC++ considers all addresses below 128 as short.
return DecoderController::decoderAddressMinMax(protocol);
}
tcb::span<const uint8_t> DCCPlusPlusInterface::decoderSpeedSteps(DecoderProtocol protocol) const
{
(void)protocol; // silence unused warning for release build
assert(protocol == DecoderProtocol::DCCShort || protocol == DecoderProtocol::DCCLong);
const auto& speedStepValues = DCCPlusPlus::Settings::speedStepValues;
// find value in array so we can create a span, using a span of a variable won't work due to the compare with prevous value in the attribute setter
if(auto it = std::find(speedStepValues.begin(), speedStepValues.end(), dccplusplus->speedSteps); it != speedStepValues.end()) /*[[likely]]/*/
return {&(*it), 1};
assert(false);
return {};
}
void DCCPlusPlusInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
@ -182,13 +229,6 @@ bool DCCPlusPlusInterface::setOnline(bool& value, bool simulation)
m_kernel->setOutputController(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}, false);
}
catch(const LogMessageException& e)
@ -202,8 +242,6 @@ bool DCCPlusPlusInterface::setOnline(bool& value, bool simulation)
{
Attributes::setEnabled({device, baudrate}, true);
m_dccplusplusPropertyChanged.disconnect();
m_kernel->stop();
m_kernel.reset();
@ -225,10 +263,12 @@ void DCCPlusPlusInterface::loaded()
Interface::loaded();
check();
updateEnabled();
}
void DCCPlusPlusInterface::destroying()
{
m_dccplusplusPropertyChanged.disconnect();
OutputController::destroying();
InputController::destroying();
DecoderController::destroying();
@ -239,32 +279,48 @@ void DCCPlusPlusInterface::worldEvent(WorldState state, WorldEvent event)
{
Interface::worldEvent(state, event);
if(m_kernel)
switch(event)
{
switch(event)
{
case WorldEvent::PowerOff:
case WorldEvent::EditEnabled:
case WorldEvent::EditDisabled:
updateEnabled();
break;
case WorldEvent::PowerOff:
if(m_kernel)
{
m_kernel->powerOff();
m_kernel->emergencyStop();
break;
}
break;
case WorldEvent::PowerOn:
case WorldEvent::PowerOn:
if(m_kernel)
{
m_kernel->powerOn();
break;
}
break;
case WorldEvent::Stop:
case WorldEvent::Stop:
if(m_kernel)
{
m_kernel->emergencyStop();
break;
}
updateEnabled();
break;
case WorldEvent::Run:
case WorldEvent::Run:
if(m_kernel)
{
m_kernel->powerOn();
m_kernel->clearEmergencyStop();
restoreDecoderSpeed();
break;
}
restoreDecoderSpeed();
updateEnabled();
break;
default:
break;
}
default:
break;
}
}
@ -276,15 +332,6 @@ void DCCPlusPlusInterface::check() const
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)
{
@ -298,3 +345,11 @@ void DCCPlusPlusInterface::idChanged(const std::string& newId)
if(m_kernel)
m_kernel->setLogId(newId);
}
void DCCPlusPlusInterface::updateEnabled()
{
const bool editable = contains(m_world.state, WorldState::Edit);
const bool stopped = !contains(m_world.state, WorldState::Run);
Attributes::setEnabled(dccplusplus->speedSteps, editable && stopped);
}

Datei anzeigen

@ -63,6 +63,8 @@ class DCCPlusPlusInterface final
void idChanged(const std::string& newId) final;
void updateEnabled();
protected:
bool setOnline(bool& value, bool simulation) final;
@ -74,6 +76,9 @@ class DCCPlusPlusInterface final
DCCPlusPlusInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:

Datei anzeigen

@ -69,6 +69,12 @@ ECoSInterface::ECoSInterface(World& world, std::string_view _id)
m_interfaceItems.insertBefore(outputs, notes);
}
tcb::span<const DecoderProtocol> ECoSInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 4> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong, DecoderProtocol::Motorola, DecoderProtocol::Selectrix};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
void ECoSInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)

Datei anzeigen

@ -73,6 +73,7 @@ class ECoSInterface final
ECoSInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:

Datei anzeigen

@ -29,6 +29,7 @@
#include "../identification/list/identificationlist.hpp"
#include "../identification/identification.hpp"
#include "../programming/lncv/lncvprogrammer.hpp"
#include "../protocol/dcc/dcc.hpp"
#include "../protocol/loconet/kernel.hpp"
#include "../protocol/loconet/settings.hpp"
#include "../protocol/loconet/iohandler/serialiohandler.hpp"
@ -131,6 +132,21 @@ bool LocoNetInterface::immPacket(tcb::span<uint8_t> dccPacket, uint8_t repeat)
return false;
}
tcb::span<const DecoderProtocol> LocoNetInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> LocoNetInterface::decoderAddressMinMax(DecoderProtocol protocol) const
{
if(protocol == DecoderProtocol::DCCLong)
{
return {DCC::addressLongStart, DCC::addressLongMax};
}
return DecoderController::decoderAddressMinMax(protocol);
}
void LocoNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)

Datei anzeigen

@ -94,6 +94,8 @@ class LocoNetInterface final
bool immPacket(tcb::span<uint8_t> dccPacket, uint8_t repeat);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:

Datei anzeigen

@ -156,6 +156,42 @@ XpressNetInterface::XpressNetInterface(World& world, std::string_view _id)
updateVisible();
}
tcb::span<const DecoderProtocol> XpressNetInterface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 2> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> XpressNetInterface::decoderAddressMinMax(DecoderProtocol protocol) const
{
switch(protocol)
{
case DecoderProtocol::DCCShort:
return {XpressNet::shortAddressMin, XpressNet::shortAddressMax};
case DecoderProtocol::DCCLong:
return {XpressNet::longAddressMin, XpressNet::longAddressMax};
default: /*[[unlikely]]*/
return DecoderController::decoderAddressMinMax(protocol);
}
}
tcb::span<const uint8_t> XpressNetInterface::decoderSpeedSteps(DecoderProtocol protocol) const
{
static constexpr std::array<uint8_t, 4> dccSpeedSteps{{14, 27, 28, 128}}; // XpressNet also support 27 steps
switch(protocol)
{
case DecoderProtocol::DCCShort:
case DecoderProtocol::DCCLong:
return dccSpeedSteps;
default: /*[[unlikely]]*/
return DecoderController::decoderSpeedSteps(protocol);
}
}
void XpressNetInterface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)

Datei anzeigen

@ -82,6 +82,9 @@ class XpressNetInterface final
XpressNetInterface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
tcb::span<const uint8_t> decoderSpeedSteps(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:

Datei anzeigen

@ -24,6 +24,7 @@
#include "../decoder/list/decoderlist.hpp"
#include "../input/list/inputlist.hpp"
#include "../output/list/outputlist.hpp"
#include "../protocol/dcc/dcc.hpp"
#include "../protocol/z21/clientkernel.hpp"
#include "../protocol/z21/clientsettings.hpp"
#include "../protocol/z21/messages.hpp"
@ -87,6 +88,21 @@ Z21Interface::Z21Interface(World& world, std::string_view _id)
m_interfaceItems.insertBefore(firmwareVersion, notes);
}
tcb::span<const DecoderProtocol> Z21Interface::decoderProtocols() const
{
static constexpr std::array<DecoderProtocol, 3> protocols{DecoderProtocol::DCCShort, DecoderProtocol::DCCLong, DecoderProtocol::Motorola};
return tcb::span<const DecoderProtocol>{protocols.data(), protocols.size()};
}
std::pair<uint16_t, uint16_t> Z21Interface::decoderAddressMinMax(DecoderProtocol protocol) const
{
if(protocol == DecoderProtocol::DCCLong)
{
return {DCC::addressLongStart, DCC::addressLongMax};
}
return DecoderController::decoderAddressMinMax(protocol);
}
void Z21Interface::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
if(m_kernel)

Datei anzeigen

@ -73,6 +73,8 @@ class Z21Interface final
Z21Interface(World& world, std::string_view _id);
// DecoderController:
tcb::span<const DecoderProtocol> decoderProtocols() const final;
std::pair<uint16_t, uint16_t> decoderAddressMinMax(DecoderProtocol protocol) const final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// InputController:

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
* Copyright (C) 2021,2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,6 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_DCC_DCC_HPP
#include <cstdint>
#include <traintastic/enum/decoderprotocol.hpp>
#include "../../../utils/inrange.hpp"
namespace DCC {
@ -39,6 +40,11 @@ constexpr bool isLongAddress(uint16_t address)
return inRange(address, addressLongStart, addressLongMax);
}
constexpr DecoderProtocol getProtocol(uint16_t address)
{
return isLongAddress(address) ? DecoderProtocol::DCCLong : DecoderProtocol::DCCShort;
}
}
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
* Copyright (C) 2021,2023 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,8 +26,6 @@
namespace DCCPlusPlus {
constexpr std::array<uint8_t, 2> speedStepValues{28, 128};
Settings::Settings(Object& _parent, std::string_view parentPropertyName)
: SubObject(_parent, parentPropertyName)
, useEx{this, "use_ex", true, PropertyFlags::ReadWrite | PropertyFlags::Store}
@ -39,6 +37,7 @@ Settings::Settings(Object& _parent, std::string_view parentPropertyName)
m_interfaceItems.add(useEx);
Attributes::addDisplayName(speedSteps, DisplayName::Hardware::speedSteps);
Attributes::addEnabled(speedSteps, false);
Attributes::addValues(speedSteps, speedStepValues);
m_interfaceItems.add(speedSteps);

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2021 Reinder Feenstra
* Copyright (C) 2021,2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -39,6 +39,8 @@ class Settings : public SubObject
static constexpr uint16_t startupDelayMax = 60'000;
public:
static constexpr std::array<uint8_t, 2> speedStepValues{28, 128};
Property<bool> useEx;
Property<uint8_t> speedSteps;
Property<uint16_t> startupDelay;

Datei anzeigen

@ -30,6 +30,7 @@
#include "object/switchmanager.hpp"
#include "object/feedbackmanager.hpp"
#include "object/feedback.hpp"
#include "../../protocol/dcc/dcc.hpp"
#include "../../decoder/decoder.hpp"
#include "../../decoder/decoderchangeflags.hpp"
#include "../../input/inputcontroller.hpp"
@ -46,14 +47,14 @@
namespace ECoS {
static constexpr DecoderProtocol toDecoderProtocol(LocomotiveProtocol value)
static constexpr DecoderProtocol toDecoderProtocol(LocomotiveProtocol locomotiveProtocol, uint16_t address)
{
switch(value)
switch(locomotiveProtocol)
{
case LocomotiveProtocol::DCC14:
case LocomotiveProtocol::DCC28:
case LocomotiveProtocol::DCC128:
return DecoderProtocol::DCC;
return DCC::getProtocol(address);
case LocomotiveProtocol::MM14:
case LocomotiveProtocol::MM27:
@ -67,7 +68,7 @@ static constexpr DecoderProtocol toDecoderProtocol(LocomotiveProtocol value)
case LocomotiveProtocol::MMFKT:
break;
}
return DecoderProtocol::Custom;
return DecoderProtocol::None;
}
Kernel::Kernel(const Config& config, bool simulation)
@ -264,9 +265,9 @@ Locomotive* Kernel::getLocomotive(DecoderProtocol protocol, uint16_t address, ui
auto* l = dynamic_cast<Locomotive*>(item.second.get());
return
l &&
(protocol == DecoderProtocol::Auto || protocol == toDecoderProtocol(l->protocol())) &&
protocol == toDecoderProtocol(l->protocol(), l->address()) &&
address == l->address() &&
(speedSteps == 0 || speedSteps == l->speedSteps());
speedSteps == l->speedSteps();
});
if(it != m_objects.end())

Datei anzeigen

@ -1029,7 +1029,7 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
case LocoNetF9F28::IMMPacket:
{
const bool longAddress = (decoder.address > DCC::addressShortMax) || decoder.longAddress;
const bool longAddress = decoder.protocol == DecoderProtocol::DCCLong;
if(functionNumber <= 12)
{
@ -1317,7 +1317,7 @@ void Kernel::clearLocoSlot(uint8_t slot)
std::shared_ptr<Decoder> Kernel::getDecoder(uint16_t address)
{
assert(isEventLoopThread());
return m_decoderController->getDecoder(DecoderProtocol::DCC, address, DCC::isLongAddress(address), true);
return m_decoderController->getDecoder(DCC::getProtocol(address), address);
}
void Kernel::setIOHandler(std::unique_ptr<IOHandler> handler)

Datei anzeigen

@ -473,11 +473,7 @@ void Kernel::heartbeatTimeoutExpired(const boost::system::error_code& ec)
std::shared_ptr<Decoder> Kernel::getDecoder(uint16_t address, bool longAddress) const
{
const auto& decoderList = *m_world.decoders;
std::shared_ptr<Decoder> decoder;
if(longAddress)
decoder = decoderList.getDecoder(DecoderProtocol::DCC, address, longAddress);
if(!decoder)
decoder = decoderList.getDecoder(DecoderProtocol::Auto, address);
std::shared_ptr<Decoder> decoder = decoderList.getDecoder(longAddress ? DecoderProtocol::DCCLong : DecoderProtocol::DCCShort, address);
if(!decoder)
decoder = decoderList.getDecoder(address);
return decoder;
@ -530,7 +526,7 @@ void Kernel::throttleUnsubscribe(uint16_t throttleId, std::pair<uint16_t, bool>
void Kernel::throttleDecoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber)
{
const std::pair<uint16_t, bool> key(decoder.address, decoder.longAddress);
const std::pair<uint16_t, bool> key(decoder.address, decoder.protocol == DecoderProtocol::DCCLong);
if(has(changes, DecoderChangeFlags::Direction | DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::SpeedSteps | DecoderChangeFlags::Throttle))
{

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -306,7 +306,7 @@ void Kernel::receiveFrom(std::string_view message, IOHandler::ClientId clientId)
if(!throttle)
return;
switch(throttle->acquire(DecoderProtocol::DCC, address.address, address.isLong, steal))
switch(throttle->acquire(address.isLong ? DecoderProtocol::DCCLong : DecoderProtocol::DCCShort, address.address, steal))
{
case Throttle::AcquireResult::Success:
{

Datei anzeigen

@ -268,7 +268,7 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
if(m_config.useEmergencyStopLocomotiveCommand && changes == DecoderChangeFlags::EmergencyStop && decoder.emergencyStop)
{
postSend(EmergencyStopLocomotive(decoder.address, decoder.longAddress));
postSend(EmergencyStopLocomotive(decoder.address));
}
else if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::Throttle | DecoderChangeFlags::SpeedSteps))
{
@ -277,7 +277,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
case 14:
postSend(SpeedAndDirectionInstruction14(
decoder.address,
decoder.longAddress,
decoder.emergencyStop,
decoder.direction,
Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, 14),
@ -287,7 +286,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
case 27:
postSend(SpeedAndDirectionInstruction27(
decoder.address,
decoder.longAddress,
decoder.emergencyStop,
decoder.direction,
Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, 27)));
@ -296,22 +294,22 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
case 28:
postSend(SpeedAndDirectionInstruction28(
decoder.address,
decoder.longAddress,
decoder.emergencyStop,
decoder.direction,
Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, 28)));
break;
case 126:
case 128:
default:
postSend(SpeedAndDirectionInstruction128(
decoder.address,
decoder.longAddress,
decoder.emergencyStop,
decoder.direction,
Decoder::throttleToSpeedStep<uint8_t>(decoder.throttle, 126)));
break;
default:
assert(false);
break;
}
}
else if(has(changes, DecoderChangeFlags::FunctionValue))
@ -320,7 +318,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(FunctionInstructionGroup1(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(0),
decoder.getFunctionValue(1),
decoder.getFunctionValue(2),
@ -331,7 +328,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(FunctionInstructionGroup2(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(5),
decoder.getFunctionValue(6),
decoder.getFunctionValue(7),
@ -341,7 +337,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(FunctionInstructionGroup3(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(9),
decoder.getFunctionValue(10),
decoder.getFunctionValue(11),
@ -353,7 +348,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(RocoMultiMAUS::FunctionInstructionF13F20(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(13),
decoder.getFunctionValue(14),
decoder.getFunctionValue(15),
@ -367,7 +361,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(FunctionInstructionGroup4(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(13),
decoder.getFunctionValue(14),
decoder.getFunctionValue(15),
@ -382,7 +375,6 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
{
postSend(FunctionInstructionGroup5(
decoder.address,
decoder.longAddress,
decoder.getFunctionValue(21),
decoder.getFunctionValue(22),
decoder.getFunctionValue(23),

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -31,6 +31,11 @@
namespace XpressNet {
constexpr uint16_t shortAddressMin = 1;
constexpr uint16_t shortAddressMax = 99;
constexpr uint16_t longAddressMin = 100;
constexpr uint16_t longAddressMax = 9999;
constexpr uint8_t idFeedbackBroadcast = 0x40;
struct Message;
@ -238,18 +243,18 @@ struct EmergencyStopLocomotive : Message
uint8_t addressLow;
uint8_t checksum;
EmergencyStopLocomotive(uint16_t address, bool longAddress)
EmergencyStopLocomotive(uint16_t address)
{
header = 0x92;
if(longAddress)
if(address >= longAddressMin)
{
assert(address >= 1 && address <= 9999);
assert(address >= longAddressMin && address <= longAddressMax);
addressHigh = 0xC0 | address >> 8;
addressLow = address & 0xff;
}
else
{
assert(address >= 1 && address <= 127);
assert(address >= shortAddressMin && address <= shortAddressMax);
addressHigh = 0x00;
addressLow = address & 0x7f;
}
@ -263,18 +268,18 @@ struct LocomotiveInstruction : Message
uint8_t addressHigh;
uint8_t addressLow;
LocomotiveInstruction(uint16_t address, bool longAddress)
LocomotiveInstruction(uint16_t address)
{
header = 0xE4;
if(longAddress)
if(address >= longAddressMin)
{
assert(address >= 1 && address <= 9999);
assert(address >= longAddressMin && address <= longAddressMax);
addressHigh = 0xc0 | address >> 8;
addressLow = address & 0xff;
}
else
{
assert(address >= 1 && address <= 127);
assert(address >= shortAddressMin && address <= shortAddressMax);
addressHigh = 0x00;
addressLow = address & 0x7f;
}
@ -287,8 +292,8 @@ struct SpeedAndDirectionInstruction : LocomotiveInstruction
uint8_t speedAndDirection;
uint8_t checksum;
SpeedAndDirectionInstruction(uint16_t address, bool longAddress, bool emergencyStop, Direction direction) :
LocomotiveInstruction(address, longAddress)
SpeedAndDirectionInstruction(uint16_t address, bool emergencyStop, Direction direction) :
LocomotiveInstruction(address)
{
assert(direction != Direction::Unknown);
speedAndDirection = emergencyStop ? 0x01 : 0x00;
@ -299,8 +304,8 @@ struct SpeedAndDirectionInstruction : LocomotiveInstruction
struct SpeedAndDirectionInstruction14 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction14(uint16_t address, bool longAddress, bool emergencyStop, Direction direction, uint8_t speedStep, bool fl) :
SpeedAndDirectionInstruction(address, longAddress, emergencyStop, direction)
SpeedAndDirectionInstruction14(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep, bool fl) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 14);
identification = 0x10;
@ -314,8 +319,8 @@ struct SpeedAndDirectionInstruction14 : SpeedAndDirectionInstruction
struct SpeedAndDirectionInstruction27 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction27(uint16_t address, bool longAddress, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, longAddress, emergencyStop, direction)
SpeedAndDirectionInstruction27(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 27);
identification = 0x11;
@ -327,8 +332,8 @@ struct SpeedAndDirectionInstruction27 : SpeedAndDirectionInstruction
struct SpeedAndDirectionInstruction28 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction28(uint16_t address, bool longAddress, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, longAddress, emergencyStop, direction)
SpeedAndDirectionInstruction28(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 28);
identification = 0x12;
@ -340,8 +345,8 @@ struct SpeedAndDirectionInstruction28 : SpeedAndDirectionInstruction
struct SpeedAndDirectionInstruction128 : SpeedAndDirectionInstruction
{
SpeedAndDirectionInstruction128(uint16_t address, bool longAddress, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, longAddress, emergencyStop, direction)
SpeedAndDirectionInstruction128(uint16_t address, bool emergencyStop, Direction direction, uint8_t speedStep) :
SpeedAndDirectionInstruction(address, emergencyStop, direction)
{
assert(speedStep <= 126);
identification = 0x13;
@ -356,8 +361,8 @@ struct FunctionInstructionGroup : LocomotiveInstruction
uint8_t functions = 0x00;
uint8_t checksum;
FunctionInstructionGroup(uint16_t address, bool longAddress, uint8_t group) :
LocomotiveInstruction(address, longAddress)
FunctionInstructionGroup(uint16_t address, uint8_t group) :
LocomotiveInstruction(address)
{
assert(group >= 1 && group <= 5);
identification = (group == 5) ? 0x28 : (0x1F + group);
@ -366,8 +371,8 @@ struct FunctionInstructionGroup : LocomotiveInstruction
struct FunctionInstructionGroup1 : FunctionInstructionGroup
{
FunctionInstructionGroup1(uint16_t address, bool longAddress, bool f0, bool f1, bool f2, bool f3, bool f4) :
FunctionInstructionGroup(address, longAddress, 1)
FunctionInstructionGroup1(uint16_t address, bool f0, bool f1, bool f2, bool f3, bool f4) :
FunctionInstructionGroup(address, 1)
{
if(f0)
functions |= 0x10;
@ -386,8 +391,8 @@ struct FunctionInstructionGroup1 : FunctionInstructionGroup
struct FunctionInstructionGroup2 : FunctionInstructionGroup
{
FunctionInstructionGroup2(uint16_t address, bool longAddress, bool f5, bool f6, bool f7, bool f8) :
FunctionInstructionGroup(address, longAddress, 2)
FunctionInstructionGroup2(uint16_t address, bool f5, bool f6, bool f7, bool f8) :
FunctionInstructionGroup(address, 2)
{
if(f5)
functions |= 0x01;
@ -404,8 +409,8 @@ struct FunctionInstructionGroup2 : FunctionInstructionGroup
struct FunctionInstructionGroup3 : FunctionInstructionGroup
{
FunctionInstructionGroup3(uint16_t address, bool longAddress, bool f9, bool f10, bool f11, bool f12) :
FunctionInstructionGroup(address, longAddress, 3)
FunctionInstructionGroup3(uint16_t address, bool f9, bool f10, bool f11, bool f12) :
FunctionInstructionGroup(address, 3)
{
if(f9)
functions |= 0x01;
@ -422,8 +427,8 @@ struct FunctionInstructionGroup3 : FunctionInstructionGroup
struct FunctionInstructionGroup4 : FunctionInstructionGroup
{
FunctionInstructionGroup4(uint16_t address, bool longAddress, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
FunctionInstructionGroup(address, longAddress, 4)
FunctionInstructionGroup4(uint16_t address, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
FunctionInstructionGroup(address, 4)
{
if(f13)
functions |= 0x01;
@ -448,8 +453,8 @@ struct FunctionInstructionGroup4 : FunctionInstructionGroup
struct FunctionInstructionGroup5 : FunctionInstructionGroup
{
FunctionInstructionGroup5(uint16_t address, bool longAddress, bool f21, bool f22, bool f23, bool f24, bool f25, bool f26, bool f27, bool f28) :
FunctionInstructionGroup(address, longAddress, 5)
FunctionInstructionGroup5(uint16_t address, bool f21, bool f22, bool f23, bool f24, bool f25, bool f26, bool f27, bool f28) :
FunctionInstructionGroup(address, 5)
{
if(f21)
functions |= 0x01;
@ -514,8 +519,8 @@ namespace RocoMultiMAUS
uint8_t functions = 0x00;
uint8_t checksum;
FunctionInstructionF13F20(uint16_t address, bool longAddress, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
LocomotiveInstruction(address, longAddress)
FunctionInstructionF13F20(uint16_t address, bool f13, bool f14, bool f15, bool f16, bool f17, bool f18, bool f19, bool f20) :
LocomotiveInstruction(address)
{
identification = 0xF3;

Datei anzeigen

@ -328,7 +328,7 @@ void ClientKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags cha
if(has(changes, DecoderChangeFlags::EmergencyStop | DecoderChangeFlags::Direction | DecoderChangeFlags::Throttle | DecoderChangeFlags::SpeedSteps))
{
LanXSetLocoDrive cmd;
cmd.setAddress(decoder.address, decoder.longAddress);
cmd.setAddress(decoder.address, decoder.protocol == DecoderProtocol::DCCLong);
switch(decoder.speedSteps)
{
@ -381,7 +381,7 @@ void ClientKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags cha
if(functionNumber <= LanXSetLocoFunction::functionNumberMax)
if(const auto& f = decoder.getFunction(functionNumber))
postSend(LanXSetLocoFunction(
decoder.address, decoder.longAddress,
decoder.address, decoder.protocol == DecoderProtocol::DCCLong,
static_cast<uint8_t>(functionNumber),
f->value ? LanXSetLocoFunction::SwitchType::On : LanXSetLocoFunction::SwitchType::Off));
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -282,7 +282,7 @@ std::string toString(const Message& message, bool raw)
LanXLocoInfo::LanXLocoInfo(const Decoder& decoder) :
LanXLocoInfo()
{
setAddress(decoder.address, decoder.longAddress);
setAddress(decoder.address, decoder.protocol == DecoderProtocol::DCCLong);
setSpeedSteps(decoder.speedSteps);
setDirection(decoder.direction);
if(decoder.emergencyStop)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,6 +24,7 @@
#include "messages.hpp"
#include "../xpressnet/messages.hpp"
#include "../../decoder/list/decoderlist.hpp"
#include "../../protocol/dcc/dcc.hpp"
#include "../../../core/eventloop.hpp"
#include "../../../log/log.hpp"
@ -333,9 +334,7 @@ LanSystemStateDataChanged ServerKernel::getLanSystemStateDataChanged() const
std::shared_ptr<Decoder> ServerKernel::getDecoder(uint16_t address, bool longAddress) const
{
auto decoder = m_decoderList->getDecoder(DecoderProtocol::DCC, address, longAddress);
if(!decoder)
decoder = m_decoderList->getDecoder(DecoderProtocol::Auto, address);
auto decoder = m_decoderList->getDecoder(longAddress ? DecoderProtocol::DCCLong : DecoderProtocol::DCCShort, address);
if(!decoder)
decoder = m_decoderList->getDecoder(address);
return decoder;
@ -398,7 +397,7 @@ void ServerKernel::unsubscribe(IOHandler::ClientId clientId, std::pair<uint16_t,
void ServerKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags /*changes*/, uint32_t /*functionNumber*/)
{
const std::pair<uint16_t, bool> key(decoder.address, decoder.longAddress);
const std::pair<uint16_t, bool> key(decoder.address, decoder.protocol == DecoderProtocol::DCCLong);
const LanXLocoInfo message(decoder);
EventLoop::call(

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -48,14 +48,12 @@ HardwareThrottle::HardwareThrottle(std::shared_ptr<ThrottleController> controlle
m_interfaceItems.add(interface);
}
Throttle::AcquireResult HardwareThrottle::acquire(DecoderProtocol protocol, uint16_t address, bool isDCCLongAddress, bool steal)
Throttle::AcquireResult HardwareThrottle::acquire(DecoderProtocol protocol, uint16_t address, bool steal)
{
assert(m_world.decoders.value());
auto& decoderList = *m_world.decoders.value();
auto decoder = decoderList.getDecoder(protocol, address, isDCCLongAddress);
if(!decoder)
decoder = decoderList.getDecoder(DecoderProtocol::Auto, address);
auto decoder = decoderList.getDecoder(protocol, address);
if(!decoder)
decoder = decoderList.getDecoder(address);
if(!decoder)

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2022 Reinder Feenstra
* Copyright (C) 2022-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -47,7 +47,7 @@ class HardwareThrottle : public Throttle
HardwareThrottle(std::shared_ptr<ThrottleController> controller, World& world, std::string_view _id);
AcquireResult acquire(DecoderProtocol protocol, uint16_t address, bool isDCCLongAddress = false, bool steal = false);
AcquireResult acquire(DecoderProtocol protocol, uint16_t address, bool steal = false);
};
#endif

Datei anzeigen

@ -25,7 +25,7 @@
#include <lua.hpp>
#include "enum.hpp"
#include "../../src/enum/decoderprotocol.hpp"
#include <traintastic/enum/decoderprotocol.hpp>
#include "../../src/enum/direction.hpp"
#include "../../src/enum/directioncontrolstate.hpp"
#include <traintastic/enum/identificationeventtype.hpp>

Datei anzeigen

@ -86,7 +86,8 @@ int main(int argc, char* argv[])
}
#endif
Locale::instance = new Locale(getLocalePath() / "en-us.lang");
const auto localePath = getLocalePath();
Locale::instance = new Locale(localePath / "en-us.lang", new Locale(localePath / "neutral.lang"));
if(enableConsoleLogger)
Log::enableConsoleLogger();

Datei anzeigen

@ -217,7 +217,15 @@ Traintastic::RunStatus Traintastic::run(const std::string& worldUUID, bool simul
world->powerOn();
}
EventLoop::exec();
try
{
EventLoop::exec();
}
catch(const std::exception& e)
{
Log::log(id, LogMessage::F1008_EVENTLOOP_CRASHED_X, e.what());
return ExitFailure;
}
return m_restart ? Restart : ExitSuccess;
}

Datei anzeigen

@ -207,7 +207,16 @@ void WorldLoader::createObject(ObjectData& objectData)
if(startsWith(classId, Interfaces::classIdPrefix))
objectData.object = Interfaces::create(*m_world, classId, id);
else if(classId == Decoder::classId)
{
if(objectData.json["protocol"].get<std::string_view>() == "dcc") //! \todo Remove in v0.4
{
if(objectData.json["long_address"].get<bool>())
objectData.json["protocol"] = "dcc_long";
else
objectData.json["protocol"] = "dcc_short";
}
objectData.object = Decoder::create(*m_world, id);
}
else if(classId == Input::classId)
objectData.object = Input::create(*m_world, id);
else if(classId == Output::classId)

Datei anzeigen

@ -28,23 +28,23 @@
enum class DecoderProtocol : uint8_t
{
Auto = 0,
DCC = 1,
None = 0,
DCCShort = 1,
Motorola = 2,
MFX = 3,
Selectrix = 4,
//FMZ = 5,
Custom = 255,
DCCLong = 6,
};
TRAINTASTIC_ENUM(DecoderProtocol, "decoder_protocol", 6,
{
{DecoderProtocol::Auto, "auto"},
{DecoderProtocol::DCC, "dcc"},
{DecoderProtocol::None, "none"},
{DecoderProtocol::DCCShort, "dcc_short"},
{DecoderProtocol::Motorola, "motorola"},
{DecoderProtocol::MFX, "mfx"},
{DecoderProtocol::Selectrix, "selectrix"},
{DecoderProtocol::Custom, "custom"},
{DecoderProtocol::DCCLong, "dcc_long"},
});
#endif

Datei anzeigen

@ -134,7 +134,6 @@ enum class LogMessage : uint32_t
W1003_READING_WORLD_X_FAILED_LIBARCHIVE_ERROR_X_X = LogMessageOffset::warning + 1003,
W2001_RECEIVED_MALFORMED_DATA_DROPPED_X_BYTES = LogMessageOffset::warning + 2001,
W2002_COMMAND_STATION_DOESNT_SUPPORT_FUNCTIONS_ABOVE_FX = LogMessageOffset::warning + 2002,
W2003_COMMAND_STATION_DOESNT_SUPPORT_X_SPEEDSTEPS_USING_X = LogMessageOffset::warning + 2003,
W2004_INPUT_ADDRESS_X_IS_INVALID = LogMessageOffset::warning + 2004,
W2005_OUTPUT_ADDRESS_X_IS_INVALID = LogMessageOffset::warning + 2005,
W2006_COMMAND_STATION_DOES_NOT_SUPPORT_LOCO_SLOT_X = LogMessageOffset::warning + 2006,
@ -191,8 +190,6 @@ enum class LogMessage : uint32_t
C1012_UNKNOWN_CLASS_X_CANT_RECREATE_OBJECT_X = LogMessageOffset::critical + 1012,
C1013_CANT_LOAD_WORLD_SAVED_WITH_NEWER_VERSION_REQUIRES_AT_LEAST_X = LogMessageOffset::critical + 1013,
C2001_ADDRESS_ALREADY_USED_AT_X = LogMessageOffset::critical + 2001,
C2002_DCCPLUSPLUS_ONLY_SUPPORTS_THE_DCC_PROTOCOL = LogMessageOffset::critical + 2002,
C2003_DCCPLUSPLUS_DOESNT_SUPPORT_DCC_LONG_ADDRESSES_BELOW_128 = LogMessageOffset::critical + 2003,
C2004_CANT_GET_FREE_SLOT = LogMessageOffset::critical + 2004,
C9999_X = LogMessageOffset::critical + 9999,
@ -204,6 +201,7 @@ enum class LogMessage : uint32_t
F1005_OPENING_UDP_SOCKET_FAILED_X = LogMessageOffset::fatal + 1005,
F1006_UDP_SOCKET_ADDRESS_REUSE_FAILED_X = LogMessageOffset::fatal + 1006,
F1007_BINDING_UDP_SOCKET_FAILED_X = LogMessageOffset::fatal + 1007,
F1008_EVENTLOOP_CRASHED_X = LogMessageOffset::fatal + 1008,
F9001_CREATING_LUA_STATE_FAILED = LogMessageOffset::fatal + 9001,
F9002_RUNNING_SCRIPT_FAILED_X = LogMessageOffset::fatal + 9002,
F9003_CALLING_FUNCTION_FAILED_X = LogMessageOffset::fatal + 9003,

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -30,7 +30,7 @@ if __name__ == "__main__":
import re
path = os.path.realpath(os.path.dirname(__file__))
for item in os.scandir(path):
if re.match(r'^[a-z]{2}-[a-z]{2}\.json$', item.name) is not None:
if re.match(r'^([a-z]{2}-[a-z]{2}|neutral)\.json$', item.name) is not None:
filename_json = os.path.join(path, item.name)
filename_lang = os.path.splitext(filename_json)[0] + '.lang'
if not os.path.exists(filename_lang) or os.path.getmtime(filename_json) > os.path.getmtime(filename_lang):

Datei anzeigen

@ -0,0 +1,370 @@
[
{
"term": "class_id:interface.dccplusplus",
"definition": "DCC++"
},
{
"term": "class_id:interface.ecos",
"definition": "ECoS"
},
{
"term": "class_id:interface.hsi88",
"definition": "HSI-88"
},
{
"term": "class_id:interface.loconet",
"definition": "LocoNet"
},
{
"term": "class_id:interface.traintastic_diy",
"definition": "Traintastic DIY"
},
{
"term": "class_id:interface.withrottle",
"definition": "WiThrottle"
},
{
"term": "class_id:interface.wlanmaus",
"definition": "WLANmaus"
},
{
"term": "class_id:interface.xpressnet",
"definition": "XpressNet"
},
{
"term": "class_id:interface.z21",
"definition": "Z21"
},
{
"term": "direction_control_state:a_to_b",
"definition": "A \u2192 B"
},
{
"term": "direction_control_state:b_to_a",
"definition": "A \u2190 B"
},
{
"term": "direction_control_state:both",
"definition": "A \u21c4 B"
},
{
"term": "direction_control_state:none",
"definition": "A \ud83d\udec7 B"
},
{
"term": "hardware:dcc",
"definition": "DCC"
},
{
"term": "hardware:dccplusplus",
"definition": "DCC++"
},
{
"term": "hardware:ecos",
"definition": "ECoS"
},
{
"term": "hardware:loconet",
"definition": "LocoNet"
},
{
"term": "hardware:motorola",
"definition": "Motorola"
},
{
"term": "hardware:s88",
"definition": "S88"
},
{
"term": "hardware:xpressnet",
"definition": "XpressNet"
},
{
"term": "hardware:z21",
"definition": "Z21"
},
{
"term": "interface.dccplusplus:dcc_plus_plus",
"definition": "DCC++(EX)"
},
{
"term": "interface.ecos:ecos",
"definition": "ECoS"
},
{
"term": "interface.traintastic_diy:traintastic_diy",
"definition": "Traintastic DIY"
},
{
"term": "interface.withrottle:withrottle",
"definition": "WiThrottle"
},
{
"term": "length_unit:ft",
"definition": "ft"
},
{
"term": "length_unit:in",
"definition": "in"
},
{
"term": "length_unit:m",
"definition": "m"
},
{
"term": "length_unit:mm",
"definition": "mm"
},
{
"term": "length_unit:yd",
"definition": "yd"
},
{
"term": "loconet_command_station:digikeijs_dr5000",
"definition": "Digikeijs DR5000"
},
{
"term": "loconet_command_station:uhlenbrock_ibcom",
"definition": "Uhlenbrock IB-Com"
},
{
"term": "loconet_command_station:uhlenbrock_intellibox",
"definition": "Uhlenbrock Intellibox"
},
{
"term": "loconet_interface_type:lbserver",
"definition": "LBServer"
},
{
"term": "loconet_interface_type:z21",
"definition": "Z21"
},
{
"term": "loconet_serial_interface:digikeijs_dr5000",
"definition": "Digikeijs DR5000"
},
{
"term": "loconet_serial_interface:rosoft_loconet_interface",
"definition": "RoSoft LocoNet interface"
},
{
"term": "loconet_settings:f9_f28",
"definition": "F9-F28"
},
{
"term": "loconet_settings:pcap",
"definition": "PCAP"
},
{
"term": "message:C9999",
"definition": "%1"
},
{
"term": "message:D0000",
"definition": "%1"
},
{
"term": "message:D9999",
"definition": "%1"
},
{
"term": "message:E9999",
"definition": "%1"
},
{
"term": "message:F9999",
"definition": "%1"
},
{
"term": "message:I1001",
"definition": "Traintastic v%1"
},
{
"term": "message:I1006",
"definition": "%1"
},
{
"term": "message:I1007",
"definition": "%1"
},
{
"term": "message:I1008",
"definition": "%1"
},
{
"term": "message:I2004",
"definition": "HSI-88: %1"
},
{
"term": "message:I2005",
"definition": "%1"
},
{
"term": "message:I9002",
"definition": "%1"
},
{
"term": "message:I9999",
"definition": "%1"
},
{
"term": "message:N9999",
"definition": "%1"
},
{
"term": "message:W9999",
"definition": "%1"
},
{
"term": "power_unit:hp",
"definition": "hp"
},
{
"term": "power_unit:kw",
"definition": "kW"
},
{
"term": "power_unit:mw",
"definition": "MW"
},
{
"term": "power_unit:w",
"definition": "W"
},
{
"term": "ratio_unit:percent",
"definition": "%"
},
{
"term": "ratio_unit:ratio",
"definition": ""
},
{
"term": "speed_unit:kmph",
"definition": "km\/h"
},
{
"term": "speed_unit:mph",
"definition": "mph"
},
{
"term": "speed_unit:mps",
"definition": "m\/s"
},
{
"term": "volume_unit:gallon_uk",
"definition": "gal (UK)"
},
{
"term": "volume_unit:gallon_us",
"definition": "gal (US)"
},
{
"term": "volume_unit:l",
"definition": "l"
},
{
"term": "volume_unit:m3",
"definition": "m\u00b3"
},
{
"term": "weight_unit:kg",
"definition": "kg"
},
{
"term": "weight_unit:long_tons",
"definition": "Long tons"
},
{
"term": "weight_unit:short_tons",
"definition": "Short tons"
},
{
"term": "weight_unit:ton",
"definition": "ton"
},
{
"term": "world_scale:h0",
"definition": "H0 (1:87)"
},
{
"term": "world_scale:n",
"definition": "N (1:160)"
},
{
"term": "world_scale:tt",
"definition": "TT (1:120)"
},
{
"term": "world_scale:z",
"definition": "Z (1:220)"
},
{
"term": "xpressnet_command_station:digikeijs_dr5000",
"definition": "Digikeijs DR5000"
},
{
"term": "xpressnet_command_station:roco_10764",
"definition": "Roco 10764"
},
{
"term": "xpressnet_serial_interface_type:digikeijs_dr5000",
"definition": "Digikeijs DR5000"
},
{
"term": "xpressnet_serial_interface_type:lenz_li100",
"definition": "Lenz LI100"
},
{
"term": "xpressnet_serial_interface_type:lenz_li100f",
"definition": "Lenz LI100F"
},
{
"term": "xpressnet_serial_interface_type:lenz_li101f",
"definition": "Lenz LI101F"
},
{
"term": "xpressnet_serial_interface_type:lenz_liusb",
"definition": "Lenz LIUSB"
},
{
"term": "xpressnet_serial_interface_type:rosoft_s88xpressnetli",
"definition": "RoSoft s88XpressNetLI"
},
{
"term": "xpressnet_serial_interface:lenz_li100",
"definition": "Lenz LI100"
},
{
"term": "xpressnet_serial_interface:lenz_li100f",
"definition": "Lenz LI100F"
},
{
"term": "xpressnet_serial_interface:lenz_li101f",
"definition": "Lenz LI101F"
},
{
"term": "xpressnet_serial_interface:rosoft_s88xpressnetli",
"definition": "RoSoft s88XPressNetLI"
},
{
"term": "class_id:interface.marklin_can",
"definition": "Märklin CAN"
},
{
"term": "decoder_protocol:mfx",
"definition": "MFX"
},
{
"term": "decoder_protocol:selectrix",
"definition": "Selectrix"
},
{
"term": "marklin_can_interface_type:tcp",
"definition": "TCP"
},
{
"term": "marklin_can_interface_type:udp",
"definition": "UDP"
}
]

Datei-Diff unterdrückt, da er zu groß ist Diff laden