z21: added output support
Dieser Commit ist enthalten in:
Ursprung
a20a62649d
Commit
260799d6dc
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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
|
||||
|
||||
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren