z21: added output support

Dieser Commit ist enthalten in:
Reinder Feenstra 2022-02-14 22:34:51 +01:00
Ursprung a20a62649d
Commit 260799d6dc
6 geänderte Dateien mit 168 neuen und 2 gelöschten Zeilen

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -33,18 +33,22 @@
#include "../../utils/inrange.hpp"
#include "../../world/world.hpp"
constexpr auto outputListColumns = OutputListColumn::Id | OutputListColumn::Name | OutputListColumn::Address;
Z21Interface::Z21Interface(World& world, std::string_view _id)
: Interface(world, _id)
, hostname{this, "hostname", "192.168.1.203", PropertyFlags::ReadWrite | PropertyFlags::Store}
, port{this, "port", 21105, PropertyFlags::ReadWrite | PropertyFlags::Store}
, z21{this, "z21", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
, decoders{this, "decoders", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, outputs{this, "outputs", nullptr, PropertyFlags::ReadOnly | PropertyFlags::NoStore | PropertyFlags::SubObject}
, hardwareType{this, "hardware_type", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, serialNumber{this, "serial_number", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
, firmwareVersion{this, "firmware_version", "", PropertyFlags::ReadOnly | PropertyFlags::NoStore}
{
z21.setValueInternal(std::make_shared<Z21::ClientSettings>(*this, z21.name()));
decoders.setValueInternal(std::make_shared<DecoderList>(*this, decoders.name()));
outputs.setValueInternal(std::make_shared<OutputList>(*this, outputs.name(), outputListColumns));
Attributes::addDisplayName(hostname, DisplayName::IP::hostname);
Attributes::addEnabled(hostname, !online);
@ -60,6 +64,9 @@ Z21Interface::Z21Interface(World& world, std::string_view _id)
Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders);
m_interfaceItems.insertBefore(decoders, notes);
Attributes::addDisplayName(outputs, DisplayName::Hardware::outputs);
m_interfaceItems.insertBefore(outputs, notes);
Attributes::addCategory(hardwareType, Category::info);
m_interfaceItems.insertBefore(hardwareType, notes);
@ -92,6 +99,30 @@ void Z21Interface::decoderChanged(const Decoder& decoder, DecoderChangeFlags cha
m_kernel->decoderChanged(decoder, changes, functionNumber);
}
bool Z21Interface::addOutput(Output& output)
{
const bool success = OutputController::addOutput(output);
if(success)
outputs->addObject(output.shared_ptr<Output>());
return success;
}
bool Z21Interface::removeOutput(Output& output)
{
const bool success = OutputController::removeOutput(output);
if(success)
outputs->removeObject(output.shared_ptr<Output>());
return success;
}
bool Z21Interface::setOutputValue(uint32_t channel, uint32_t address, bool value)
{
return
m_kernel &&
inRange(address, outputAddressMinMax(channel)) &&
m_kernel->setOutput(static_cast<uint16_t>(address), value);
}
bool Z21Interface::setOnline(bool& value, bool simulation)
{
if(!m_kernel && value)
@ -195,7 +226,9 @@ bool Z21Interface::setOnline(bool& value, bool simulation)
void Z21Interface::addToWorld()
{
Interface::addToWorld();
m_world.decoderControllers->add(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
m_world.outputControllers->add(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
}
void Z21Interface::destroying()
@ -206,7 +239,15 @@ void Z21Interface::destroying()
decoder->interface = nullptr;
}
for(const auto& output : *outputs)
{
assert(output->interface.value() == std::dynamic_pointer_cast<OutputController>(shared_from_this()));
output->interface = nullptr;
}
m_world.decoderControllers->remove(std::dynamic_pointer_cast<DecoderController>(shared_from_this()));
m_world.outputControllers->remove(std::dynamic_pointer_cast<OutputController>(shared_from_this()));
Interface::destroying();
}

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
* Copyright (C) 2019-2022 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -28,6 +28,8 @@
#include "../protocol/z21/clientsettings.hpp"
#include "../decoder/decodercontroller.hpp"
#include "../decoder/decoderlist.hpp"
#include "../output/outputcontroller.hpp"
#include "../output/list/outputlist.hpp"
#include "../../core/objectproperty.hpp"
/**
@ -36,6 +38,7 @@
class Z21Interface final
: public Interface
, public DecoderController
, public OutputController
{
CLASS_ID("interface.z21")
DEFAULT_ID("z21")
@ -61,6 +64,7 @@ class Z21Interface final
Property<uint16_t> port;
ObjectProperty<Z21::ClientSettings> z21;
ObjectProperty<DecoderList> decoders;
ObjectProperty<OutputList> outputs;
Property<std::string> hardwareType;
Property<std::string> serialNumber;
Property<std::string> firmwareVersion;
@ -71,6 +75,12 @@ class Z21Interface final
[[nodiscard]] bool addDecoder(Decoder& decoder) final;
[[nodiscard]] bool removeDecoder(Decoder& decoder) final;
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) final;
// OutputController:
std::pair<uint32_t, uint32_t> outputAddressMinMax(uint32_t /*channel*/) const final { return {Z21::ClientKernel::outputAddressMin, Z21::ClientKernel::outputAddressMax}; }
[[nodiscard]] bool addOutput(Output& output) final;
[[nodiscard]] bool removeOutput(Output& output) final;
[[nodiscard]] bool setOutputValue(uint32_t channel, uint32_t address, bool value) final;
};
#endif

Datei anzeigen

@ -28,6 +28,7 @@
#include "../../input/inputcontroller.hpp"
#include "../../../core/eventloop.hpp"
#include "../../../log/log.hpp"
#include "../../../utils/inrange.hpp"
namespace Z21 {
@ -279,6 +280,19 @@ void ClientKernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags cha
}
}
bool ClientKernel::setOutput(uint16_t address, bool value)
{
assert(inRange<uint32_t>(address, outputAddressMin, outputAddressMax));
m_ioContext.post(
[this, address, value]()
{
send(LanXSetTurnout(address, value, true));
});
return true;
}
void ClientKernel::onStart()
{
// reset all state values

Datei anzeigen

@ -72,6 +72,9 @@ class ClientKernel final : public Kernel
void keepAliveTimerExpired(const boost::system::error_code& ec);
public:
static constexpr uint32_t outputAddressMin = 1;
static constexpr uint32_t outputAddressMax = 4096;
/**
* @brief Create kernel and IO handler
* @param[in] config Z21 client configuration
@ -171,6 +174,14 @@ class ClientKernel final : public Kernel
/**
*/
void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber);
/**
*
* @param[in] address Output address, \ref outputAddressMin .. \ref outputAddressMax
* @param[in] value Output value: \c true is on, \c false is off.
* @return \c true if send successful, \c false otherwise.
*/
bool setOutput(uint16_t address, bool value);
};
}

Datei anzeigen

@ -89,6 +89,24 @@ std::string toString(const Message& message, bool raw)
raw = true;
break;
case 0x43:
{
const auto& getTurnoutInfo = static_cast<const LanXGetTurnoutInfo&>(message);
s = "LAN_X_GET_TURNOUT_INFO";
s.append(" address=").append(std::to_string(getTurnoutInfo.address()));
break;
}
case 0x53:
{
const auto& setTurnout = static_cast<const LanXSetTurnout&>(message);
s = "LAN_X_SET_TURNOUT";
s.append(" linear_address=").append(std::to_string(setTurnout.linearAddress()));
s.append(" address=").append(std::to_string(setTurnout.address()));
s.append(" port=").append(std::to_string(setTurnout.port()));
s.append(" activate=").append(setTurnout.activate() ? "yes" : "no");
s.append(" queue=").append(setTurnout.queue() ? "yes" : "no");
break;
}
case 0x61:
if(message == LanXBCTrackPowerOff())
s = "LAN_X_BC_TRACK_POWER_OFF";

Datei anzeigen

@ -367,8 +367,80 @@ static_assert(sizeof(LanXSetTrackPowerOn) == 7);
// LAN_X_MWRITE_BYTE
// LAN_X_GET_TURNOUT_INFO
struct LanXGetTurnoutInfo : LanX
{
uint8_t db0;
uint8_t db1;
uint8_t checksum;
LanXGetTurnoutInfo(uint16_t address)
: LanX(sizeof(LanXGetTurnoutInfo), 0x43)
, db0(address >> 8)
, db1(address & 0xFF)
{
calcChecksum();
}
uint16_t address() const
{
return (static_cast<uint16_t>(db0) << 8) | db1;
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(LanXGetTurnoutInfo) == 8);
// LAN_X_SET_TURNOUT
struct LanXSetTurnout : LanX
{
static constexpr uint8_t db2Port = 0x01;
static constexpr uint8_t db2Activate = 0x08;
static constexpr uint8_t db2Queue = 0x20;
uint8_t db0;
uint8_t db1;
uint8_t db2 = 0x80;
uint8_t checksum;
LanXSetTurnout(uint16_t linearAddress, bool activate, bool queue = false)
: LanX(sizeof(LanXSetTurnout), 0x53)
, db0(linearAddress >> 9)
, db1((linearAddress >> 1) & 0xFF)
{
if(queue)
db2 |= db2Queue;
if(activate)
db2 |= db2Activate;
if(linearAddress & 0x0001)
db2 |= db2Port;
calcChecksum();
}
uint16_t address() const
{
return (static_cast<uint16_t>(db0) << 8) | db1;
}
uint16_t linearAddress() const
{
return (address() << 1) | port();
}
bool activate() const
{
return db2 & db2Queue;
}
bool queue() const
{
return db2 & db2Queue;
}
uint8_t port() const
{
return db2 & db2Port;
}
} ATTRIBUTE_PACKED;
static_assert(sizeof(LanXSetTurnout) == 9);
// LAN_X_SET_STOP
struct LanXSetStop : LanX