From 8f636f2be4ef1015939cea2a32acd7c189833ad3 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Mon, 13 Nov 2023 23:25:16 +0100 Subject: [PATCH] board: made signals aware of path reservation #WIP --- client/src/utils/enum.cpp | 2 + ...{signalpath.cpp => abstractsignalpath.cpp} | 125 ++++++++++++++---- ...{signalpath.hpp => abstractsignalpath.hpp} | 40 ++++-- server/src/board/map/blockpath.cpp | 2 +- server/src/board/map/blockpath.hpp | 2 +- .../rail/signal/signal2aspectrailtile.cpp | 45 ++++++- .../rail/signal/signal3aspectrailtile.cpp | 59 ++++++--- .../board/tile/rail/signal/signalrailtile.cpp | 49 ++++++- .../board/tile/rail/signal/signalrailtile.hpp | 21 ++- shared/src/traintastic/enum/autoyesno.hpp | 51 +++++++ shared/translations/en-us.json | 16 +++ 11 files changed, 345 insertions(+), 67 deletions(-) rename server/src/board/map/{signalpath.cpp => abstractsignalpath.cpp} (61%) rename server/src/board/map/{signalpath.hpp => abstractsignalpath.hpp} (80%) create mode 100644 shared/src/traintastic/enum/autoyesno.hpp diff --git a/client/src/utils/enum.cpp b/client/src/utils/enum.cpp index c7ae7450..1348b0b0 100644 --- a/client/src/utils/enum.cpp +++ b/client/src/utils/enum.cpp @@ -23,6 +23,7 @@ #include "enum.hpp" #include "../network/abstractproperty.hpp" #include +#include #include #include #include @@ -85,6 +86,7 @@ QVector enumValues(const QString& enumName) QString translateEnum(const QString& enumName, qint64 value) { + TRANSLATE_ENUM(AutoYesNo) TRANSLATE_ENUM(Color) TRANSLATE_ENUM(DecoderFunctionFunction) TRANSLATE_ENUM(DecoderFunctionType) diff --git a/server/src/board/map/signalpath.cpp b/server/src/board/map/abstractsignalpath.cpp similarity index 61% rename from server/src/board/map/signalpath.cpp rename to server/src/board/map/abstractsignalpath.cpp index 824db948..8b85c5b3 100644 --- a/server/src/board/map/signalpath.cpp +++ b/server/src/board/map/abstractsignalpath.cpp @@ -1,5 +1,5 @@ /** - * server/src/board/map/signalpath.cpp + * server/src/board/map/abstractsignalpath.cpp * * This file is part of the traintastic source code. * @@ -20,45 +20,108 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "signalpath.hpp" +#include "abstractsignalpath.hpp" #include "../../core/objectproperty.tpp" #include "../tile/rail/blockrailtile.hpp" #include "../tile/rail/turnout/turnoutrailtile.hpp" #include "../tile/rail/directioncontrolrailtile.hpp" #include "../tile/rail/onewayrailtile.hpp" #include "../tile/rail/linkrailtile.hpp" -#include "../map/signalpath.hpp" +#include "../tile/rail/signal/signalrailtile.hpp" +#include "../map/abstractsignalpath.hpp" #include "../../train/train.hpp" // FIXME: required due to forward declaration -SignalPath::SignalPath(const Node& signalNode, size_t blocksAhead, std::function&)> onEvaluated) - : m_signalNode{signalNode} - , m_onEvaluated{std::move(onEvaluated)} +AbstractSignalPath::AbstractSignalPath(SignalRailTile& signal) + : m_signal{signal} { - if(auto link = signalNode.getLink(1)) - m_root = findBlocks(signalNode, *link, blocksAhead); - evaluate(); } -SignalPath::~SignalPath() +AbstractSignalPath::AbstractSignalPath(SignalRailTile& signal, size_t blocksAhead)//, std::function&)> onEvaluated) + : AbstractSignalPath(signal) +{ + const auto& signalNode = signal.node()->get(); + if(auto link = signalNode.getLink(1); link && blocksAhead != 0) + m_root = findBlocks(signalNode, *link, blocksAhead); + + { + // Require a reserved path if there is at least one turnout in the path to the next block. + // NOTE: this can be overriden using the signal's requireReservation property. + const AbstractSignalPath::Item* item = m_root.get(); + while(item) + { + if(dynamic_cast(item)) + { + break; + } + if(dynamic_cast(item)) + { + m_requireReservation = true; + break; + } + item = item->next().get(); + } + } +} + +AbstractSignalPath::~AbstractSignalPath() { for(auto& connection : m_connections) + { connection.disconnect(); + } } -void SignalPath::evaluate() +void AbstractSignalPath::evaluate() +{ + const bool stop = !signal().hasReservedPath() && requireReservation(); + + setAspect(stop ? SignalAspect::Stop : determineAspect()); +} + +bool AbstractSignalPath::requireReservation() const +{ + return (m_signal.requireReservation == AutoYesNo::Yes || (m_signal.requireReservation == AutoYesNo::Auto && m_requireReservation)); +} + +void AbstractSignalPath::getBlockStates(tcb::span blockStates) const +{ + size_t i = 0; + const Item* item = m_root.get(); + while(item && i < blockStates.size()) + { + if(const auto* blockItem = dynamic_cast(item)) + { + blockStates[i] = blockItem->blockState(); + i++; + } + item = item->next().get(); + } + + if(i < blockStates.size()) + { + std::fill(blockStates.data() + i, blockStates.data() + blockStates.size(), BlockState::Unknown); + } +} + +std::shared_ptr AbstractSignalPath::getBlock(size_t index) const { - std::vector blockStates; const Item* item = m_root.get(); while(item) { if(const auto* blockItem = dynamic_cast(item)) - blockStates.emplace_back(blockItem->blockState()); + { + if(index == 0) + { + return blockItem->block(); + } + index--; + } item = item->next().get(); } - m_onEvaluated(blockStates); + return {}; } -std::unique_ptr SignalPath::findBlocks(const Node& node, const Link& link, size_t blocksAhead) +std::unique_ptr AbstractSignalPath::findBlocks(const Node& node, const Link& link, size_t blocksAhead) { const auto& nextNode = link.getNext(node); auto tile = nextNode.tile().shared_ptr(); @@ -75,7 +138,7 @@ std::unique_ptr SignalPath::findBlocks(const Node& node, if(blocksAhead > 1) if(const auto& nextLink = otherLink(nextNode, link)) next = findBlocks(nextNode, *nextLink, blocksAhead - 1); - return std::unique_ptr{new BlockItem(block, std::move(next))}; + return std::unique_ptr{new BlockItem(block, std::move(next))}; } if(auto turnout = std::dynamic_pointer_cast(tile)) { @@ -92,7 +155,7 @@ std::unique_ptr SignalPath::findBlocks(const Node& node, } if(!next.empty()) - return std::unique_ptr{new TurnoutItem(turnout, std::move(next))}; + return std::unique_ptr{new TurnoutItem(turnout, std::move(next))}; } else if(auto direction = std::dynamic_pointer_cast(tile)) { @@ -109,7 +172,7 @@ std::unique_ptr SignalPath::findBlocks(const Node& node, evaluate(); })); - return std::unique_ptr{ + return std::unique_ptr{ new DirectionControlItem( direction, nextNode.getLink(0).get() == &link ? DirectionControlState::AtoB : DirectionControlState::BtoA, @@ -150,7 +213,7 @@ std::unique_ptr SignalPath::findBlocks(const Node& node, { if(const auto& nextLink = otherLink(nextNode, link)) { - if(&nextNode == &m_signalNode) + if(&nextNode == &m_signal.node()->get()) return {}; // we're reached oursels return findBlocks(nextNode, *nextLink, blocksAhead); @@ -161,15 +224,27 @@ std::unique_ptr SignalPath::findBlocks(const Node& node, return {}; } - -BlockState SignalPath::BlockItem::blockState() const +void AbstractSignalPath::setAspect(SignalAspect value) const { - if(auto block = m_block.lock()) - return block->state; + m_signal.setAspect(value); +} + + +std::shared_ptr AbstractSignalPath::BlockItem::block() const noexcept +{ + return m_block.lock(); +} + +BlockState AbstractSignalPath::BlockItem::blockState() const +{ + if(auto blk = block()) + { + return blk->state; + } return BlockState::Unknown; } -const std::unique_ptr& SignalPath::DirectionControlItem::next() const +const std::unique_ptr& AbstractSignalPath::DirectionControlItem::next() const { if(auto directionControl = m_directionControl.lock()) { @@ -180,7 +255,7 @@ const std::unique_ptr& SignalPath::DirectionControlItem: return noItem; } -const std::unique_ptr& SignalPath::TurnoutItem::next() const +const std::unique_ptr& AbstractSignalPath::TurnoutItem::next() const { if(auto turnout = m_turnout.lock()) if(auto it = m_next.find(turnout->position.value()); it != m_next.end()) diff --git a/server/src/board/map/signalpath.hpp b/server/src/board/map/abstractsignalpath.hpp similarity index 80% rename from server/src/board/map/signalpath.hpp rename to server/src/board/map/abstractsignalpath.hpp index 2d727c12..40b2d1db 100644 --- a/server/src/board/map/signalpath.hpp +++ b/server/src/board/map/abstractsignalpath.hpp @@ -34,9 +34,19 @@ class TurnoutRailTile; enum class TurnoutPosition : uint8_t; class DirectionControlRailTile; enum class DirectionControlState : uint8_t; +class SignalRailTile; +enum class SignalAspect : uint8_t; -class SignalPath : public Path +class AbstractSignalPath : public Path { + private: + SignalRailTile& m_signal; + + AbstractSignalPath(const AbstractSignalPath&) = delete; + AbstractSignalPath& operator =(const AbstractSignalPath&) = delete; + + void setAspect(SignalAspect value) const; + private: class Item { @@ -73,6 +83,7 @@ class SignalPath : public Path return m_next; } + std::shared_ptr block() const noexcept; BlockState blockState() const; }; @@ -110,15 +121,10 @@ class SignalPath : public Path const std::unique_ptr& next() const final; }; - const Node& m_signalNode; std::unique_ptr m_root; + bool m_requireReservation = false; std::vector m_connections; - std::function&)> m_onEvaluated; - SignalPath(const SignalPath&) = delete; - SignalPath& operator =(const SignalPath&) = delete; - - void evaluate(); std::unique_ptr findBlocks(const Node& node, const Link& link, size_t blocksAhead); inline std::unique_ptr findBlocks(const Node& node, const std::shared_ptr& link, size_t blocksAhead) @@ -128,9 +134,25 @@ class SignalPath : public Path return {}; } + protected: + inline const SignalRailTile& signal() const + { + return m_signal; + } + + bool requireReservation() const; + + virtual SignalAspect determineAspect() const = 0; + + void getBlockStates(tcb::span blockStates) const; + std::shared_ptr getBlock(size_t index) const; + public: - SignalPath(const Node& signalNode, size_t blocksAhead, std::function&)> onEvaluated); - ~SignalPath(); + AbstractSignalPath(SignalRailTile& signal); + AbstractSignalPath(SignalRailTile& signal, size_t blocksAhead); + ~AbstractSignalPath(); + + void evaluate(); }; #endif diff --git a/server/src/board/map/blockpath.cpp b/server/src/board/map/blockpath.cpp index 1b8fb728..1ebcf636 100644 --- a/server/src/board/map/blockpath.cpp +++ b/server/src/board/map/blockpath.cpp @@ -401,7 +401,7 @@ bool BlockPath::reserve(const std::shared_ptr& train, bool dryRun) { if(auto signal = signalWeak.lock()) { - if(!signal->reserve(SignalRailTile::Pass{}, dryRun)) + if(!signal->reserve(shared_from_this(), dryRun)) { assert(dryRun); return false; diff --git a/server/src/board/map/blockpath.hpp b/server/src/board/map/blockpath.hpp index a75b6387..69e78647 100644 --- a/server/src/board/map/blockpath.hpp +++ b/server/src/board/map/blockpath.hpp @@ -48,7 +48,7 @@ class Train; /** * \brief A path between two blocks */ -class BlockPath : public Path +class BlockPath : public Path, public std::enable_shared_from_this { private: BlockRailTile& m_fromBlock; diff --git a/server/src/board/tile/rail/signal/signal2aspectrailtile.cpp b/server/src/board/tile/rail/signal/signal2aspectrailtile.cpp index 3c7e2418..edd58dde 100644 --- a/server/src/board/tile/rail/signal/signal2aspectrailtile.cpp +++ b/server/src/board/tile/rail/signal/signal2aspectrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 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 @@ -21,7 +21,8 @@ */ #include "signal2aspectrailtile.hpp" -#include "../../../map/signalpath.hpp" +#include "../../../map/abstractsignalpath.hpp" +#include "../../../map/blockpath.hpp" #include "../../../../core/attributes.hpp" #include "../../../../core/method.tpp" #include "../../../../core/objectproperty.tpp" @@ -29,6 +30,39 @@ static const std::array aspectValues = {SignalAspect::Stop, SignalAspect::Proceed, SignalAspect::Unknown}; static const std::array setAspectValues = {SignalAspect::Stop, SignalAspect::Proceed}; +namespace +{ + class SignalPath : public AbstractSignalPath + { + protected: + SignalAspect determineAspect() const final + { + std::array states; + getBlockStates(states); + + if(!requireReservation() && states[0] == BlockState::Free) + { + return SignalAspect::Proceed; + } + if(states[0] == BlockState::Reserved) + { + const auto path = signal().reservedPath(); + if(path && path->toBlock() == getBlock(0)) + { + return SignalAspect::Proceed; + } + } + return SignalAspect::Stop; + } + + public: + SignalPath(Signal2AspectRailTile& signal) + : AbstractSignalPath(signal, 1) + { + } + }; +} + Signal2AspectRailTile::Signal2AspectRailTile(World& world, std::string_view _id) : SignalRailTile(world, _id, TileId::RailSignal2Aspect) { @@ -43,9 +77,6 @@ Signal2AspectRailTile::Signal2AspectRailTile(World& world, std::string_view _id) void Signal2AspectRailTile::boardModified() { - m_signalPath = std::make_unique(m_node, 1, - [this](const std::vector& states) - { - setAspect(!states.empty() && states[0] == BlockState::Free ? SignalAspect::Proceed : SignalAspect::Stop); - }); + m_signalPath = std::make_unique(*this); + SignalRailTile::boardModified(); } diff --git a/server/src/board/tile/rail/signal/signal3aspectrailtile.cpp b/server/src/board/tile/rail/signal/signal3aspectrailtile.cpp index 6ee2c177..21fed4a6 100644 --- a/server/src/board/tile/rail/signal/signal3aspectrailtile.cpp +++ b/server/src/board/tile/rail/signal/signal3aspectrailtile.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2020-2022 Reinder Feenstra + * Copyright (C) 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 @@ -21,7 +21,8 @@ */ #include "signal3aspectrailtile.hpp" -#include "../../../map/signalpath.hpp" +#include "../../../map/abstractsignalpath.hpp" +#include "../../../map/blockpath.hpp" #include "../../../../core/attributes.hpp" #include "../../../../core/method.tpp" #include "../../../../core/objectproperty.tpp" @@ -29,6 +30,45 @@ static const std::array aspectValues = {SignalAspect::Stop, SignalAspect::ProceedReducedSpeed, SignalAspect::Proceed, SignalAspect::Unknown}; static const std::array setAspectValues = {SignalAspect::Stop, SignalAspect::ProceedReducedSpeed, SignalAspect::Proceed}; +namespace +{ + class SignalPath : public AbstractSignalPath + { + protected: + SignalAspect determineAspect() const final + { + std::array states; + getBlockStates(states); + + if(!requireReservation() && states[0] == BlockState::Free) + { + if(states[1] == BlockState::Free) + { + return SignalAspect::Proceed; + } + return SignalAspect::ProceedReducedSpeed; + } + if(states[0] == BlockState::Reserved) + { + const auto path = signal().reservedPath(); + if(path && path->toBlock() == getBlock(0)) + { + //! \todo check next block reserved and signal state + + return SignalAspect::ProceedReducedSpeed; + } + } + return SignalAspect::Stop; + } + + public: + SignalPath(Signal3AspectRailTile& signal) + : AbstractSignalPath(signal, 2) + { + } + }; +} + Signal3AspectRailTile::Signal3AspectRailTile(World& world, std::string_view _id) : SignalRailTile(world, _id, TileId::RailSignal3Aspect) { @@ -43,17 +83,6 @@ Signal3AspectRailTile::Signal3AspectRailTile(World& world, std::string_view _id) void Signal3AspectRailTile::boardModified() { - m_signalPath = std::make_unique(m_node, 2, - [this](const std::vector& states) - { - if(!states.empty() && states[0] == BlockState::Free) - { - if(states.size() >= 2 && states[1] == BlockState::Free) - setAspect(SignalAspect::Proceed); - else - setAspect(SignalAspect::ProceedReducedSpeed); - } - else - setAspect(SignalAspect::Stop); - }); + m_signalPath = std::make_unique(*this); + SignalRailTile::boardModified(); } diff --git a/server/src/board/tile/rail/signal/signalrailtile.cpp b/server/src/board/tile/rail/signal/signalrailtile.cpp index 56ec4425..fdcbfbc3 100644 --- a/server/src/board/tile/rail/signal/signalrailtile.cpp +++ b/server/src/board/tile/rail/signal/signalrailtile.cpp @@ -21,7 +21,7 @@ */ #include "signalrailtile.hpp" -#include "../../../map/signalpath.hpp" +#include "../../../map/abstractsignalpath.hpp" #include "../../../../core/attributes.hpp" #include "../../../../core/method.tpp" #include "../../../../core/objectproperty.tpp" @@ -32,6 +32,7 @@ SignalRailTile::SignalRailTile(World& world, std::string_view _id, TileId tileId StraightRailTile(world, _id, tileId), m_node{*this, 2}, name{this, "name", std::string(_id), PropertyFlags::ReadWrite | PropertyFlags::Store | PropertyFlags::ScriptReadOnly}, + requireReservation{this, "require_reservation", AutoYesNo::Auto, PropertyFlags::ReadWrite | PropertyFlags::Store}, aspect{this, "aspect", SignalAspect::Unknown, PropertyFlags::ReadOnly | PropertyFlags::StoreState | PropertyFlags::ScriptReadOnly}, outputMap{this, "output_map", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject | PropertyFlags::NoScript}, setAspect{*this, "set_aspect", MethodFlags::ScriptCallable, [this](SignalAspect value) { return doSetAspect(value); }} @@ -42,6 +43,10 @@ SignalRailTile::SignalRailTile(World& world, std::string_view _id, TileId tileId Attributes::addEnabled(name, editable); m_interfaceItems.add(name); + Attributes::addEnabled(requireReservation, editable); + Attributes::addValues(requireReservation, autoYesNoValues); + m_interfaceItems.add(requireReservation); + Attributes::addObjectEditor(aspect, false); // aspect is added by sub class @@ -54,13 +59,25 @@ SignalRailTile::SignalRailTile(World& world, std::string_view _id, TileId tileId SignalRailTile::~SignalRailTile() = default; // default here, so we can use a forward declaration of SignalPath in the header. -bool SignalRailTile::reserve(Pass, bool dryRun) +bool SignalRailTile::hasReservedPath() const noexcept +{ + return !m_blockPath.expired(); +} + +std::shared_ptr SignalRailTile::reservedPath() const noexcept +{ + return m_blockPath.lock(); +} + +bool SignalRailTile::reserve(const std::shared_ptr& blockPath, bool dryRun) { // no conditions yet... if(!dryRun) { + m_blockPath = blockPath; RailTile::reserve(); + evaluate(); } return true; } @@ -70,8 +87,24 @@ void SignalRailTile::worldEvent(WorldState state, WorldEvent event) StraightRailTile::worldEvent(state, event); const bool editable = contains(state, WorldState::Edit); + const bool editableAndStopped = editable && !contains(state, WorldState::Run); Attributes::setEnabled(name, editable); + Attributes::setEnabled(requireReservation, editableAndStopped); + + if(event == WorldEvent::Run) + { + evaluate(); + } +} + +void SignalRailTile::boardModified() +{ + if(m_signalPath) + { + m_signalPath->evaluate(); + } + StraightRailTile::boardModified(); } bool SignalRailTile::doSetAspect(SignalAspect value) @@ -84,3 +117,15 @@ bool SignalRailTile::doSetAspect(SignalAspect value) aspect.setValueInternal(value); return true; } + +void SignalRailTile::evaluate() +{ + if(m_signalPath) /*[[likely]]*/ + { + m_signalPath->evaluate(); + } + else + { + setAspect(SignalAspect::Stop); + } +} diff --git a/server/src/board/tile/rail/signal/signalrailtile.hpp b/server/src/board/tile/rail/signal/signalrailtile.hpp index 6ef64f61..848f7e10 100644 --- a/server/src/board/tile/rail/signal/signalrailtile.hpp +++ b/server/src/board/tile/rail/signal/signalrailtile.hpp @@ -24,13 +24,15 @@ #define TRAINTASTIC_SERVER_BOARD_TILE_RAIL_SIGNAL_SIGNALRAILTILE_HPP #include "../straightrailtile.hpp" +#include #include "../../../map/node.hpp" #include "../../../../core/method.hpp" #include "../../../../enum/signalaspect.hpp" #include "../../../../core/objectproperty.hpp" #include "../../../../hardware/output/map/signaloutputmap.hpp" -class SignalPath; +class AbstractSignalPath; +class BlockPath; class SignalRailTile : public StraightRailTile { @@ -38,20 +40,22 @@ class SignalRailTile : public StraightRailTile protected: Node m_node; - std::unique_ptr m_signalPath; + std::unique_ptr m_signalPath; + std::weak_ptr m_blockPath; SignalRailTile(World& world, std::string_view _id, TileId tileId); void worldEvent(WorldState state, WorldEvent event) override; + void boardModified() override; + virtual bool doSetAspect(SignalAspect value); - public: - struct Pass - { - }; + void evaluate(); + public: Property name; + Property requireReservation; Property aspect; ObjectProperty outputMap; Method setAspect; @@ -61,7 +65,10 @@ class SignalRailTile : public StraightRailTile std::optional> node() const final { return m_node; } std::optional> node() final { return m_node; } - bool reserve(Pass, bool dryRun = false); + bool hasReservedPath() const noexcept; + std::shared_ptr reservedPath() const noexcept; + + bool reserve(const std::shared_ptr& blockPath, bool dryRun = false); }; #endif diff --git a/shared/src/traintastic/enum/autoyesno.hpp b/shared/src/traintastic/enum/autoyesno.hpp new file mode 100644 index 00000000..04361c16 --- /dev/null +++ b/shared/src/traintastic/enum/autoyesno.hpp @@ -0,0 +1,51 @@ +/** + * shared/src/traintastic/enum/autoyesno.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 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_SHARED_TRAINTASTIC_ENUM_AUTOYESNO_HPP +#define TRAINTASTIC_SHARED_TRAINTASTIC_ENUM_AUTOYESNO_HPP + +#include +#include +#include "enum.hpp" + +enum class AutoYesNo : uint8_t +{ + Auto = 0, + Yes = 1, + No = 2, +}; + +TRAINTASTIC_ENUM(AutoYesNo, "auto_yes_no", 3, +{ + {AutoYesNo::Auto, "auto"}, + {AutoYesNo::Yes, "yes"}, + {AutoYesNo::No, "no"}, +}); + +constexpr std::array autoYesNoValues +{ + AutoYesNo::Auto, + AutoYesNo::Yes, + AutoYesNo::No, +}; + +#endif diff --git a/shared/translations/en-us.json b/shared/translations/en-us.json index d10b3185..15e0e523 100644 --- a/shared/translations/en-us.json +++ b/shared/translations/en-us.json @@ -4358,5 +4358,21 @@ { "term": "message:W3002", "definition": "NX button not connected to any block" + }, + { + "term": "auto_yes_no:auto", + "definition": "Auto detect" + }, + { + "term": "auto_yes_no:yes", + "definition": "Yes" + }, + { + "term": "auto_yes_no:no", + "definition": "No" + }, + { + "term": "board_tile.rail.signal_3_aspect:require_reservation", + "definition": "Require reservation" } ] \ No newline at end of file