board: made signals aware of path reservation #WIP

Dieser Commit ist enthalten in:
Reinder Feenstra 2023-11-13 23:25:16 +01:00
Ursprung cf18271bc8
Commit 8f636f2be4
11 geänderte Dateien mit 345 neuen und 67 gelöschten Zeilen

Datei anzeigen

@ -23,6 +23,7 @@
#include "enum.hpp"
#include "../network/abstractproperty.hpp"
#include <traintastic/locale/locale.hpp>
#include <traintastic/enum/autoyesno.hpp>
#include <traintastic/enum/color.hpp>
#include <traintastic/enum/decoderfunctionfunction.hpp>
#include <traintastic/enum/decoderfunctiontype.hpp>
@ -85,6 +86,7 @@ QVector<qint64> enumValues(const QString& enumName)
QString translateEnum(const QString& enumName, qint64 value)
{
TRANSLATE_ENUM(AutoYesNo)
TRANSLATE_ENUM(Color)
TRANSLATE_ENUM(DecoderFunctionFunction)
TRANSLATE_ENUM(DecoderFunctionType)

Datei anzeigen

@ -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<void(const std::vector<BlockState>&)> 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<void(const std::vector<BlockState>&)> 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<const BlockItem*>(item))
{
break;
}
if(dynamic_cast<const TurnoutItem*>(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<BlockState> blockStates) const
{
size_t i = 0;
const Item* item = m_root.get();
while(item && i < blockStates.size())
{
if(const auto* blockItem = dynamic_cast<const BlockItem*>(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<BlockRailTile> AbstractSignalPath::getBlock(size_t index) const
{
std::vector<BlockState> blockStates;
const Item* item = m_root.get();
while(item)
{
if(const auto* blockItem = dynamic_cast<const BlockItem*>(item))
blockStates.emplace_back(blockItem->blockState());
{
if(index == 0)
{
return blockItem->block();
}
index--;
}
item = item->next().get();
}
m_onEvaluated(blockStates);
return {};
}
std::unique_ptr<const SignalPath::Item> SignalPath::findBlocks(const Node& node, const Link& link, size_t blocksAhead)
std::unique_ptr<const AbstractSignalPath::Item> AbstractSignalPath::findBlocks(const Node& node, const Link& link, size_t blocksAhead)
{
const auto& nextNode = link.getNext(node);
auto tile = nextNode.tile().shared_ptr<Tile>();
@ -75,7 +138,7 @@ std::unique_ptr<const SignalPath::Item> 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<const SignalPath::Item>{new BlockItem(block, std::move(next))};
return std::unique_ptr<const AbstractSignalPath::Item>{new BlockItem(block, std::move(next))};
}
if(auto turnout = std::dynamic_pointer_cast<TurnoutRailTile>(tile))
{
@ -92,7 +155,7 @@ std::unique_ptr<const SignalPath::Item> SignalPath::findBlocks(const Node& node,
}
if(!next.empty())
return std::unique_ptr<const SignalPath::Item>{new TurnoutItem(turnout, std::move(next))};
return std::unique_ptr<const AbstractSignalPath::Item>{new TurnoutItem(turnout, std::move(next))};
}
else if(auto direction = std::dynamic_pointer_cast<DirectionControlRailTile>(tile))
{
@ -109,7 +172,7 @@ std::unique_ptr<const SignalPath::Item> SignalPath::findBlocks(const Node& node,
evaluate();
}));
return std::unique_ptr<const SignalPath::Item>{
return std::unique_ptr<const AbstractSignalPath::Item>{
new DirectionControlItem(
direction,
nextNode.getLink(0).get() == &link ? DirectionControlState::AtoB : DirectionControlState::BtoA,
@ -150,7 +213,7 @@ std::unique_ptr<const SignalPath::Item> 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<const SignalPath::Item> 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<BlockRailTile> 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<const SignalPath::Item>& SignalPath::DirectionControlItem::next() const
const std::unique_ptr<const AbstractSignalPath::Item>& AbstractSignalPath::DirectionControlItem::next() const
{
if(auto directionControl = m_directionControl.lock())
{
@ -180,7 +255,7 @@ const std::unique_ptr<const SignalPath::Item>& SignalPath::DirectionControlItem:
return noItem;
}
const std::unique_ptr<const SignalPath::Item>& SignalPath::TurnoutItem::next() const
const std::unique_ptr<const AbstractSignalPath::Item>& AbstractSignalPath::TurnoutItem::next() const
{
if(auto turnout = m_turnout.lock())
if(auto it = m_next.find(turnout->position.value()); it != m_next.end())

Datei anzeigen

@ -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<BlockRailTile> block() const noexcept;
BlockState blockState() const;
};
@ -110,15 +121,10 @@ class SignalPath : public Path
const std::unique_ptr<const Item>& next() const final;
};
const Node& m_signalNode;
std::unique_ptr<const Item> m_root;
bool m_requireReservation = false;
std::vector<boost::signals2::connection> m_connections;
std::function<void(const std::vector<BlockState>&)> m_onEvaluated;
SignalPath(const SignalPath&) = delete;
SignalPath& operator =(const SignalPath&) = delete;
void evaluate();
std::unique_ptr<const Item> findBlocks(const Node& node, const Link& link, size_t blocksAhead);
inline std::unique_ptr<const Item> findBlocks(const Node& node, const std::shared_ptr<const Link>& 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<BlockState> blockStates) const;
std::shared_ptr<BlockRailTile> getBlock(size_t index) const;
public:
SignalPath(const Node& signalNode, size_t blocksAhead, std::function<void(const std::vector<BlockState>&)> onEvaluated);
~SignalPath();
AbstractSignalPath(SignalRailTile& signal);
AbstractSignalPath(SignalRailTile& signal, size_t blocksAhead);
~AbstractSignalPath();
void evaluate();
};
#endif

Datei anzeigen

@ -401,7 +401,7 @@ bool BlockPath::reserve(const std::shared_ptr<Train>& 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;

Datei anzeigen

@ -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<BlockPath>
{
private:
BlockRailTile& m_fromBlock;

Datei anzeigen

@ -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<SignalAspect, 3> aspectValues = {SignalAspect::Stop, SignalAspect::Proceed, SignalAspect::Unknown};
static const std::array<SignalAspect, 2> setAspectValues = {SignalAspect::Stop, SignalAspect::Proceed};
namespace
{
class SignalPath : public AbstractSignalPath
{
protected:
SignalAspect determineAspect() const final
{
std::array<BlockState, 1> 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<SignalPath>(m_node, 1,
[this](const std::vector<BlockState>& states)
{
setAspect(!states.empty() && states[0] == BlockState::Free ? SignalAspect::Proceed : SignalAspect::Stop);
});
m_signalPath = std::make_unique<SignalPath>(*this);
SignalRailTile::boardModified();
}

Datei anzeigen

@ -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<SignalAspect, 4> aspectValues = {SignalAspect::Stop, SignalAspect::ProceedReducedSpeed, SignalAspect::Proceed, SignalAspect::Unknown};
static const std::array<SignalAspect, 3> setAspectValues = {SignalAspect::Stop, SignalAspect::ProceedReducedSpeed, SignalAspect::Proceed};
namespace
{
class SignalPath : public AbstractSignalPath
{
protected:
SignalAspect determineAspect() const final
{
std::array<BlockState, 2> 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<SignalPath>(m_node, 2,
[this](const std::vector<BlockState>& 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<SignalPath>(*this);
SignalRailTile::boardModified();
}

Datei anzeigen

@ -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<BlockPath> SignalRailTile::reservedPath() const noexcept
{
return m_blockPath.lock();
}
bool SignalRailTile::reserve(const std::shared_ptr<BlockPath>& 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);
}
}

Datei anzeigen

@ -24,13 +24,15 @@
#define TRAINTASTIC_SERVER_BOARD_TILE_RAIL_SIGNAL_SIGNALRAILTILE_HPP
#include "../straightrailtile.hpp"
#include <traintastic/enum/autoyesno.hpp>
#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<SignalPath> m_signalPath;
std::unique_ptr<AbstractSignalPath> m_signalPath;
std::weak_ptr<BlockPath> 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<std::string> name;
Property<AutoYesNo> requireReservation;
Property<SignalAspect> aspect;
ObjectProperty<SignalOutputMap> outputMap;
Method<bool(SignalAspect)> setAspect;
@ -61,7 +65,10 @@ class SignalRailTile : public StraightRailTile
std::optional<std::reference_wrapper<const Node>> node() const final { return m_node; }
std::optional<std::reference_wrapper<Node>> node() final { return m_node; }
bool reserve(Pass, bool dryRun = false);
bool hasReservedPath() const noexcept;
std::shared_ptr<BlockPath> reservedPath() const noexcept;
bool reserve(const std::shared_ptr<BlockPath>& blockPath, bool dryRun = false);
};
#endif

Datei anzeigen

@ -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 <cstdint>
#include <array>
#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<AutoYesNo, 3> autoYesNoValues
{
AutoYesNo::Auto,
AutoYesNo::Yes,
AutoYesNo::No,
};
#endif

Datei anzeigen

@ -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"
}
]