train tracking: basic detection for manual controlled trains

Dieser Commit ist enthalten in:
Reinder Feenstra 2023-12-28 23:31:01 +01:00
Ursprung 29eb8402ee
Commit bcf92585b5
4 geänderte Dateien mit 149 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -316,6 +316,37 @@ bool BlockPath::operator ==(const BlockPath& other) const noexcept
(m_nxButtonTo == other.m_nxButtonTo);
}
bool BlockPath::isReady() const
{
for(const auto& [turnoutWeak, position] : m_turnouts)
{
auto turnout = turnoutWeak.lock();
if(!turnout) /*[[unlikely]]*/
{
return false;
}
if(turnout->position != position)
{
return false;
}
}
for(const auto& [directionControlWeak, state] : m_directionControls)
{
auto directionControl = directionControlWeak.lock();
if(!directionControl) /*[[unlikely]]*/
{
return false;
}
if(directionControl->state != state && directionControl->state != DirectionControlState::Both)
{
return false;
}
}
return true;
}
std::shared_ptr<NXButtonRailTile> BlockPath::nxButtonFrom() const
{
return m_nxButtonFrom.lock();

Datei anzeigen

@ -71,6 +71,9 @@ class BlockPath : public Path, public std::enable_shared_from_this<BlockPath>
bool operator ==(const BlockPath& other) const noexcept;
//! \return \c true if all turnouts are in position and direction controls are allowed to pass.
bool isReady() const;
bool hasNXButtons() const
{
return !m_nxButtonFrom.expired() && !m_nxButtonTo.expired();

Datei anzeigen

@ -214,6 +214,81 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
void BlockRailTile::inputItemValueChanged(BlockInputMapItem& item)
{
if(item.value() == SensorState::Occupied)
{
switch(state.value())
{
case BlockState::Free:
case BlockState::Unknown:
{
// Something entered the block, try to determine what it is.
if(inputMap->items.size() > 2 && (&item != inputMap->items.front().get()) && (&item != inputMap->items.back().get()))
{
// Non block edge sensor.
//! \todo log something (at least in debug)
break;
}
const bool anySide = inputMap->items.size() == 1; // block with one sensor
const BlockSide enterSide = (&item == inputMap->items.front().get()) ? BlockSide::A : BlockSide::B;
std::shared_ptr<Train> train;
BlockTrainDirection direction;
for(const auto& path : m_pathsIn)
{
if(path->toBlock().get() == this && (anySide || path->toSide() == enterSide) && !path->fromBlock().trains.empty() && path->isReady())
{
const auto status = path->fromSide() == BlockSide::A ? path->fromBlock().trains.front() : path->fromBlock().trains.back();
if(isDirectionTowardsSide(status->direction, path->fromSide()) &&
status->train &&
status->train->powered &&
status->train->mode == TrainMode::ManualUnprotected &&
!status->train->isStopped)
{
if(train) // another train?? then we don't know
{
train.reset();
//! \todo log something (at least in debug)
break;
}
else
{
train = status->train.value();
direction = path->toSide() == BlockSide::A ? BlockTrainDirection::TowardsB : BlockTrainDirection::TowardsA;
}
}
}
}
if(train)
{
auto blockStatus = TrainBlockStatus::create(*this, *train, direction);
blockStatus->train->blocks.insertInternal(0, blockStatus); // head of train
trains.appendInternal(blockStatus);
updateTrainMethodEnabled();
fireEvent<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&>(
onTrainEntered,
blockStatus->train.value(),
shared_ptr<BlockRailTile>(),
blockStatus->direction.value());
}
break;
}
case BlockState::Reserved:
break;
case BlockState::Occupied:
break;
}
}
if(inputMap->items.size() != sensorStates.size())
{
std::vector<SensorState> values;
@ -226,6 +301,40 @@ void BlockRailTile::inputItemValueChanged(BlockInputMapItem& item)
sensorStates.setValueInternal(inputMap->items.indexOf(item), item.value());
updateState();
if(item.value() == SensorState::Free && state.value() == BlockState::Reserved)
{
if(trains.size() == 1)
{
auto blockStatus = trains.front();
// Train must be in at least two blocks, else we loose it.
// Release tailing block of train only. (When using current detection not all wagons might consume power.)
if(blockStatus->train &&
blockStatus->train->blocks.size() > 1 &&
blockStatus->train->blocks.back() == blockStatus)
{
blockStatus->train->blocks.removeInternal(blockStatus);
trains.removeInternal(blockStatus);
updateTrainMethodEnabled();
updateState();
fireEvent<const std::shared_ptr<Train>&, const std::shared_ptr<BlockRailTile>&>(
onTrainLeft,
blockStatus->train.value(),
shared_ptr<BlockRailTile>(),
blockStatus->direction.value());
blockStatus->destroy();
#ifndef NDEBUG
std::weak_ptr<TrainBlockStatus> blockStatusWeak = blockStatus;
blockStatus.reset();
assert(blockStatusWeak.expired());
#endif
}
}
}
}
void BlockRailTile::identificationEvent(BlockInputMapItem& /*item*/, IdentificationEventType eventType, uint16_t identifier, Direction direction, uint8_t /*category*/)

Datei anzeigen

@ -106,6 +106,12 @@ class ObjectVectorProperty : public AbstractObjectVectorProperty
changed();
}
void insertInternal(size_t index, std::shared_ptr<T> value)
{
m_values.emplace(m_values.begin() + std::min(index, m_values.size()), std::move(value));
changed();
}
void removeInternal(const std::shared_ptr<T>& value)
{
auto it = std::find(m_values.begin(), m_values.end(), value);