Merge remote-tracking branch 'origin/master' into 11-märklin-cs2cs3-hardware-support
Dieser Commit ist enthalten in:
Commit
54ba3664a6
@ -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();
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
||||
@ -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:
|
||||
{
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
@ -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):
|
||||
|
||||
370
shared/translations/neutral.json
Normale Datei
370
shared/translations/neutral.json
Normale Datei
@ -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
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren