From 22099e80f95da23c4b4376b4e1c6b13de5d7dd6b Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Sun, 9 Jan 2022 12:57:28 +0100 Subject: [PATCH] DCC++: added accessory output support --- .../interface/dccplusplusinterface.cpp | 31 ++++++++++++++++++- .../interface/dccplusplusinterface.hpp | 12 ++++++- .../hardware/protocol/dccplusplus/kernel.cpp | 21 ++++++++++++- .../hardware/protocol/dccplusplus/kernel.hpp | 30 +++++++++++++++++- .../protocol/dccplusplus/messages.hpp | 4 +-- 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/server/src/hardware/interface/dccplusplusinterface.cpp b/server/src/hardware/interface/dccplusplusinterface.cpp index b5714341..ec7b597f 100644 --- a/server/src/hardware/interface/dccplusplusinterface.cpp +++ b/server/src/hardware/interface/dccplusplusinterface.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-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 @@ -40,9 +40,11 @@ DCCPlusPlusInterface::DCCPlusPlusInterface(const std::weak_ptr& world, st , flowControl{this, "flow_control", SerialFlowControl::None, PropertyFlags::ReadWrite | PropertyFlags::Store} , dccplusplus{this, "dccplusplus", 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} { dccplusplus.setValueInternal(std::make_shared(*this, dccplusplus.name())); decoders.setValueInternal(std::make_shared(*this, decoders.name())); + outputs.setValueInternal(std::make_shared(*this, outputs.name())); Attributes::addDisplayName(device, DisplayName::Serial::device); Attributes::addEnabled(device, !online); @@ -64,6 +66,9 @@ DCCPlusPlusInterface::DCCPlusPlusInterface(const std::weak_ptr& world, st Attributes::addDisplayName(decoders, DisplayName::Hardware::decoders); m_interfaceItems.insertBefore(decoders, notes); + + Attributes::addDisplayName(outputs, DisplayName::Hardware::outputs); + m_interfaceItems.insertBefore(outputs, notes); } bool DCCPlusPlusInterface::addDecoder(Decoder& decoder) @@ -88,6 +93,30 @@ void DCCPlusPlusInterface::decoderChanged(const Decoder& decoder, DecoderChangeF m_kernel->decoderChanged(decoder, changes, functionNumber); } +bool DCCPlusPlusInterface::addOutput(Output& output) +{ + const bool success = OutputController::addOutput(output); + if(success) + outputs->addObject(output.shared_ptr()); + return success; +} + +bool DCCPlusPlusInterface::removeOutput(Output& output) +{ + const bool success = OutputController::removeOutput(output); + if(success) + outputs->removeObject(output.shared_ptr()); + return success; +} + +bool DCCPlusPlusInterface::setOutputValue(uint32_t address, bool value) +{ + return + m_kernel && + inRange(address, outputAddressMinMax()) && + m_kernel->setOutput(static_cast(address), value); +} + bool DCCPlusPlusInterface::setOnline(bool& value) { if(!m_kernel && value) diff --git a/server/src/hardware/interface/dccplusplusinterface.hpp b/server/src/hardware/interface/dccplusplusinterface.hpp index 2dad70a2..6026549c 100644 --- a/server/src/hardware/interface/dccplusplusinterface.hpp +++ b/server/src/hardware/interface/dccplusplusinterface.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-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/dccplusplus/settings.hpp" #include "../decoder/decodercontroller.hpp" #include "../decoder/decoderlist.hpp" +#include "../output/outputcontroller.hpp" +#include "../output/list/outputlist.hpp" #include "../../core/objectproperty.hpp" #include "../../enum/serialflowcontrol.hpp" @@ -37,6 +39,7 @@ class DCCPlusPlusInterface final : public Interface , public DecoderController + , public OutputController { CLASS_ID("interface.dccplusplus") CREATE(DCCPlusPlusInterface) @@ -65,6 +68,7 @@ class DCCPlusPlusInterface final Property flowControl; ObjectProperty dccplusplus; ObjectProperty decoders; + ObjectProperty outputs; DCCPlusPlusInterface(const std::weak_ptr& world, std::string_view _id); @@ -72,6 +76,12 @@ class DCCPlusPlusInterface 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 outputAddressMinMax() const final { return {DCCPlusPlus::Kernel::outputAddressMin, DCCPlusPlus::Kernel::outputAddressMax}; } + [[nodiscard]] bool addOutput(Output& output) final; + [[nodiscard]] bool removeOutput(Output& output) final; + [[nodiscard]] bool setOutputValue(uint32_t address, bool value) final; }; #endif diff --git a/server/src/hardware/protocol/dccplusplus/kernel.cpp b/server/src/hardware/protocol/dccplusplus/kernel.cpp index 1df94ac6..a81809f9 100644 --- a/server/src/hardware/protocol/dccplusplus/kernel.cpp +++ b/server/src/hardware/protocol/dccplusplus/kernel.cpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-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 @@ -62,6 +62,7 @@ void Kernel::start() // reset all state values m_powerOn = TriState::Undefined; m_emergencyStop = TriState::Undefined; + m_outputValues.fill(TriState::Undefined); m_thread = std::thread( [this]() @@ -214,6 +215,24 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, } } +bool Kernel::setOutput(uint16_t address, bool value) +{ + assert(inRange(address, outputAddressMin, outputAddressMax)); + + m_ioContext.post( + [this, address, value]() + { + const auto index = address - outputAddressMin; + if(m_outputValues[index] != toTriState(value)) + { + m_outputValues[index] = toTriState(value); + send(Ex::setAccessory(address - outputAddressMin, value)); + } + }); + + return true; +} + void Kernel::setIOHandler(std::unique_ptr handler) { assert(handler); diff --git a/server/src/hardware/protocol/dccplusplus/kernel.hpp b/server/src/hardware/protocol/dccplusplus/kernel.hpp index 41bc5f42..2f539002 100644 --- a/server/src/hardware/protocol/dccplusplus/kernel.hpp +++ b/server/src/hardware/protocol/dccplusplus/kernel.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-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 @@ -34,6 +34,7 @@ class Decoder; enum class DecoderChangeFlags; class DecoderController; +class OutputController; namespace DCCPlusPlus { @@ -41,6 +42,10 @@ struct Message; class Kernel { + public: + static constexpr uint16_t outputAddressMin = 1; + static constexpr uint16_t outputAddressMax = 2044; + private: boost::asio::io_context m_ioContext; std::unique_ptr m_ioHandler; @@ -55,6 +60,9 @@ class Kernel DecoderController* m_decoderController; + OutputController* m_outputController; + std::array m_outputValues; + Config m_config; #ifndef NDEBUG bool m_started; @@ -176,6 +184,18 @@ class Kernel m_decoderController = decoderController; } + /** + * @brief Set the output controller + * + * @param[in] outputController The output controller + * @note This function may not be called when the kernel is running. + */ + inline void setOutputController(OutputController* outputController) + { + assert(!m_started); + m_outputController = outputController; + } + /** * @brief Start the kernel and IO handler */ @@ -225,6 +245,14 @@ class Kernel * */ void decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber); + + /** + * + * @param[in] address Output address, #outputAddressMin..#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); }; } diff --git a/server/src/hardware/protocol/dccplusplus/messages.hpp b/server/src/hardware/protocol/dccplusplus/messages.hpp index 1dc45d4c..59a55c75 100644 --- a/server/src/hardware/protocol/dccplusplus/messages.hpp +++ b/server/src/hardware/protocol/dccplusplus/messages.hpp @@ -3,7 +3,7 @@ * * This file is part of the traintastic source code. * - * Copyright (C) 2021 Reinder Feenstra + * Copyright (C) 2021-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 @@ -181,7 +181,7 @@ namespace Ex { inline std::string setAccessory(uint16_t linearAddress, bool activate) { - assert(linearAddress <= 2047); + assert(linearAddress >= 1 && linearAddress <= 2044); return std::string("