board: added basic identification/RailCom support for blocks

Dieser Commit ist enthalten in:
Reinder Feenstra 2023-10-08 17:48:16 +02:00
Ursprung 01e168b57b
Commit b5b3da9107
13 geänderte Dateien mit 193 neuen und 11 gelöschten Zeilen

Datei anzeigen

@ -1020,13 +1020,23 @@ void TilePainter::drawRailBlock(const QRectF& r, TileRotate rotate, const Object
{
if(auto* trainBlockStatus = dynamic_cast<TrainBlockStatus*>(block->trains()[0].get())) /*[[likely]]*/
{
if(const auto& train = trainBlockStatus->train()) /*[[likely]]*/
if(const auto& train = trainBlockStatus->train())
{
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsA)
label += "< ";
label += train->getPropertyValueString("name");
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsB)
label += " >";
}
else if(auto identification = trainBlockStatus->identification(); !identification.isEmpty())
{
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsA)
label += "< ";
label += identification;
if(trainBlockStatus->direction() == BlockTrainDirection::TowardsB)
label += " >";
}

Datei anzeigen

@ -45,6 +45,14 @@ BlockTrainDirection TrainBlockStatus::direction() const
return static_cast<BlockTrainDirection>(0);
}
QString TrainBlockStatus::identification() const
{
if(m_identificationProperty) /*[[likely]]*/
return m_identificationProperty->toString();
assert(false);
return {};
}
void TrainBlockStatus::created()
{
Object::created();
@ -57,6 +65,14 @@ void TrainBlockStatus::created()
emit changed();
});
m_identificationProperty = dynamic_cast<Property*>(getProperty("identification"));
if(m_identificationProperty)
connect(m_identificationProperty, &Property::valueChanged, this,
[this]()
{
emit changed();
});
if((m_trainProperty = dynamic_cast<ObjectProperty*>(getProperty("train")))) /*[[likely]]*/
{
connect(m_trainProperty, &ObjectProperty::valueChanged, this, &TrainBlockStatus::updateTrain);

Datei anzeigen

@ -39,6 +39,7 @@ class TrainBlockStatus final : public Object
private:
int m_requestId;
Property* m_directionProperty = nullptr;
Property* m_identificationProperty = nullptr;
ObjectProperty* m_trainProperty = nullptr;
ObjectPtr m_train;
@ -52,6 +53,7 @@ class TrainBlockStatus final : public Object
~TrainBlockStatus() final;
BlockTrainDirection direction() const;
QString identification() const;
const ObjectPtr& train() const
{

Datei anzeigen

@ -120,8 +120,7 @@ BlockRailTile::BlockRailTile(World& world, std::string_view _id) :
if(it == trains.end()) /*[[unlikely]]*/
return; // can't remove a train that isn't in the block
oldTrain->blocks.removeInternal(*it);
trains.removeInternal(*it);
(**it).destroy();
updateTrainMethodEnabled();
if(state == BlockState::Reserved)
@ -217,6 +216,58 @@ void BlockRailTile::inputItemValueChanged(BlockInputMapItem& item)
updateState();
}
void BlockRailTile::identificationEvent(BlockInputMapItem& item, IdentificationEventType eventType, uint16_t identifier, Direction direction, uint8_t category)
{
const auto self = shared_ptr<BlockRailTile>();
BlockTrainDirection blockDirection = BlockTrainDirection::Unknown;
if(direction != Direction::Unknown)
blockDirection = (direction == Direction::Reverse) ? BlockTrainDirection::TowardsB : BlockTrainDirection::TowardsA;
if(trains.empty())
{
switch(eventType)
{
case IdentificationEventType::Absent:
break; // nothing to do...its gone
case IdentificationEventType::Present:
//!< \todo assign train (if allowed and possible)
trains.appendInternal(TrainBlockStatus::create(*this, std::string("#").append(std::to_string(identifier)), blockDirection));
if(state == BlockState::Free || state == BlockState::Unknown)
updateState();
break;
case IdentificationEventType::Seen:
break;
}
}
else
{
switch(eventType)
{
case IdentificationEventType::Absent:
{
const auto identification = std::string("#").append(std::to_string(identifier));
for(auto& train : trains)
{
if(train->identification.value() == identification)
{
train->destroy();
updateState();
break;
}
}
break;
}
case IdentificationEventType::Present:
break;
case IdentificationEventType::Seen:
break;
}
}
}
void BlockRailTile::updateState()
{
if(!inputMap->items.empty())

Datei anzeigen

@ -77,6 +77,7 @@ class BlockRailTile : public RailTile
void getConnectors(std::vector<Connector>& connectors) const final;
void inputItemValueChanged(BlockInputMapItem& item);
void identificationEvent(BlockInputMapItem& item, IdentificationEventType eventType, uint16_t identifier, Direction direction, uint8_t category);
};
#endif

Datei anzeigen

@ -28,6 +28,11 @@ void StateObject::addToWorld(World& world, StateObject& object)
world.m_objects.emplace(object.getObjectId(), object.weak_from_this());
}
void StateObject::removeFromWorld(World& world, StateObject& object)
{
world.m_objects.erase(object.m_id);
}
StateObject::StateObject(std::string id)
: m_id{std::move(id)}
{

Datei anzeigen

@ -37,6 +37,8 @@ private:
std::string m_id;
protected:
static void removeFromWorld(World& world, StateObject& object);
void save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& state) const override;
public:

Datei anzeigen

@ -58,6 +58,18 @@ BlockInputMapItem::BlockInputMapItem(BlockInputMap& parent, uint32_t itemId) :
if(input)
inputPropertyChanged(input->value);
}}
, identification{this, "identification", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
nullptr,
[this](const std::shared_ptr<Identification>& value)
{
if(identification)
disconnectIdentification(*identification);
if(value)
connectIdentification(*value);
return true;
}}
{
auto& world = getWorld(m_parent);
const bool editable = contains(world.state.value(), WorldState::Edit);
@ -74,6 +86,9 @@ BlockInputMapItem::BlockInputMapItem(BlockInputMap& parent, uint32_t itemId) :
m_interfaceItems.add(type);
Attributes::addEnabled(invert, editable && stopped);
m_interfaceItems.add(invert);
Attributes::addEnabled(identification, editable && stopped);
Attributes::addObjectList(identification, world.identifications);
m_interfaceItems.add(identification);
}
BlockInputMapItem::~BlockInputMapItem()
@ -81,6 +96,8 @@ BlockInputMapItem::~BlockInputMapItem()
assert(!input);
assert(!m_inputPropertyChanged.connected());
assert(!m_inputDestroying.connected());
assert(!identification);
assert(!m_identificationDestroying.connected());
}
std::string BlockInputMapItem::getObjectId() const
@ -100,11 +117,14 @@ void BlockInputMapItem::loaded()
if(input)
connectInput(*input);
if(identification)
connectIdentification(*identification);
}
void BlockInputMapItem::destroying()
{
input = nullptr;
identification = nullptr;
InputMapItem::destroying();
}
@ -119,6 +139,7 @@ void BlockInputMapItem::worldEvent(WorldState state, WorldEvent event)
Attributes::setEnabled(input, editable && stopped);
Attributes::setEnabled(type, false/*editable && stopped*/);
Attributes::setEnabled(invert, editable && stopped);
Attributes::setEnabled(identification, editable && stopped);
}
void BlockInputMapItem::connectInput(Input& object)
@ -147,6 +168,29 @@ void BlockInputMapItem::inputPropertyChanged(BaseProperty& property)
setValue(toSensorState(type, input->value.value() ^ invert.value()));
}
void BlockInputMapItem::connectIdentification(Identification& object)
{
object.consumers.appendInternal(m_parent.parent().shared_from_this());
m_identificationDestroying = object.onDestroying.connect(
[this]([[maybe_unused]] Object& obj)
{
assert(identification.value().get() == &obj);
identification = nullptr;
});
m_identificationEvent = object.onEvent.connect(
[this](IdentificationEventType eventType, uint16_t identifier, Direction direction, uint8_t category)
{
static_cast<BlockRailTile&>(m_parent.parent()).identificationEvent(*this, eventType, identifier, direction, category);
});
}
void BlockInputMapItem::disconnectIdentification(Identification& object)
{
m_identificationEvent.disconnect();
m_identificationDestroying.disconnect();
object.consumers.removeInternal(m_parent.parent().shared_from_this());
}
void BlockInputMapItem::setValue(SensorState value)
{
if(m_value != value)

Datei anzeigen

@ -28,6 +28,7 @@
#include "../input.hpp"
#include "../../../enum/sensortype.hpp"
#include "../../../enum/sensorstate.hpp"
#include "../../identification/identification.hpp"
class BlockInputMap;
@ -40,11 +41,17 @@ class BlockInputMapItem final : public InputMapItem
const uint32_t m_itemId;
boost::signals2::connection m_inputDestroying;
boost::signals2::connection m_inputPropertyChanged;
boost::signals2::connection m_identificationDestroying;
boost::signals2::connection m_identificationEvent;
SensorState m_value;
void connectInput(Input& object);
void disconnectInput(Input& object);
void inputPropertyChanged(BaseProperty& property);
void connectIdentification(Identification& object);
void disconnectIdentification(Identification& object);
void setValue(SensorState value);
protected:
@ -58,6 +65,7 @@ class BlockInputMapItem final : public InputMapItem
ObjectProperty<Input> input;
Property<SensorType> type;
Property<bool> invert;
ObjectProperty<Identification> identification;
BlockInputMapItem(BlockInputMap& parent, uint32_t itemId);
~BlockInputMapItem() final;

Datei anzeigen

@ -30,21 +30,49 @@
std::shared_ptr<TrainBlockStatus> TrainBlockStatus::create(BlockRailTile& block_, Train& train_, BlockTrainDirection direction_, std::string_view id)
{
World& world = block_.world();
auto p = std::make_shared<TrainBlockStatus>(block_, train_, direction_, id.empty() ? world.getUniqueId(TrainBlockStatus::classId) : std::string{id});
auto p = std::make_shared<TrainBlockStatus>(id.empty() ? world.getUniqueId(TrainBlockStatus::classId) : std::string{id});
p->block.setValueInternal(block_.shared_ptr<BlockRailTile>());
p->train.setValueInternal(train_.shared_ptr<Train>());
p->direction.setValueInternal(direction_);
TrainBlockStatus::addToWorld(world, *p);
return p;
}
TrainBlockStatus::TrainBlockStatus(BlockRailTile& block_, Train& train_, BlockTrainDirection direction_, std::string id)
std::shared_ptr<TrainBlockStatus> TrainBlockStatus::create(BlockRailTile& block_, std::string identification_, BlockTrainDirection direction_, std::string_view id)
{
World& world = block_.world();
auto p = std::make_shared<TrainBlockStatus>(id.empty() ? world.getUniqueId(TrainBlockStatus::classId) : std::string{id});
p->block.setValueInternal(block_.shared_ptr<BlockRailTile>());
p->identification.setValueInternal(std::move(identification_));
p->direction.setValueInternal(direction_);
TrainBlockStatus::addToWorld(world, *p);
return p;
}
TrainBlockStatus::TrainBlockStatus(std::string id)
: StateObject(std::move(id))
, block{this, "block", block_.shared_ptr<BlockRailTile>(), PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, train{this, "train", train_.shared_ptr<Train>(), PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, direction{this, "direction", direction_, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, block{this, "block", nullptr, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, train{this, "train", nullptr, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, identification{this, "identification", "", PropertyFlags::ReadOnly | PropertyFlags::StoreState}
, direction{this, "direction", BlockTrainDirection::TowardsA, PropertyFlags::ReadOnly | PropertyFlags::StoreState}
{
m_interfaceItems.add(block);
m_interfaceItems.add(train);
m_interfaceItems.add(identification);
Attributes::addValues(direction, blockTrainDirectionValues);
m_interfaceItems.add(direction);
}
void TrainBlockStatus::destroying()
{
auto self = shared_ptr<TrainBlockStatus>();
if(block)
block->trains.removeInternal(self);
if(train)
train->blocks.removeInternal(self);
removeFromWorld(block->world(), *this);
StateObject::destroying();
}

Datei anzeigen

@ -36,14 +36,19 @@ class TrainBlockStatus final : public StateObject
{
CLASS_ID("train_block_status");
protected:
void destroying() final;
public:
static std::shared_ptr<TrainBlockStatus> create(BlockRailTile& block_, Train& train_, BlockTrainDirection direction_, std::string_view id = {});
static std::shared_ptr<TrainBlockStatus> create(BlockRailTile& block_, std::string identification_, BlockTrainDirection direction_, std::string_view id = {});
ObjectProperty<BlockRailTile> block;
ObjectProperty<Train> train;
Property<std::string> identification;
Property<BlockTrainDirection> direction; //!< \brief Train direction from the block perspective
TrainBlockStatus(BlockRailTile& block_, Train& train_, BlockTrainDirection direction_, std::string id);
TrainBlockStatus(std::string id);
};
#endif

Datei anzeigen

@ -29,24 +29,30 @@
enum class BlockTrainDirection : uint8_t
{
Unknown = 0,
TowardsA = 1, //!< West for horizontal blocks, South for vertical blocks.
TowardsB = 2, //!< East for horizontal blocks, North for vertical blocks.
};
TRAINTASTIC_ENUM(BlockTrainDirection, "block_train_direction", 2,
TRAINTASTIC_ENUM(BlockTrainDirection, "block_train_direction", 3,
{
{BlockTrainDirection::Unknown, "unknown"},
{BlockTrainDirection::TowardsA, "towards_a"},
{BlockTrainDirection::TowardsB, "towards_b"}
});
constexpr std::array<BlockTrainDirection, 2> blockTrainDirectionValues
constexpr std::array<BlockTrainDirection, 3> blockTrainDirectionValues
{
BlockTrainDirection::Unknown,
BlockTrainDirection::TowardsA,
BlockTrainDirection::TowardsB,
};
constexpr BlockTrainDirection operator !(BlockTrainDirection value)
{
if(value == BlockTrainDirection::Unknown)
return BlockTrainDirection::Unknown;
return (value == BlockTrainDirection::TowardsA) ? BlockTrainDirection::TowardsB : BlockTrainDirection::TowardsA;
}

Datei anzeigen

@ -4318,5 +4318,9 @@
{
"term": "message:E3001",
"definition": "Can't delete rail vehicle when in active train"
},
{
"term": "input_map_item.block:identification",
"definition": "Identification"
}
]