diff --git a/server/src/hardware/interface/dccplusplusinterface.cpp b/server/src/hardware/interface/dccplusplusinterface.cpp index 7d8e57ff..457cdab3 100644 --- a/server/src/hardware/interface/dccplusplusinterface.cpp +++ b/server/src/hardware/interface/dccplusplusinterface.cpp @@ -114,6 +114,12 @@ bool DCCPlusPlusInterface::removeInput(Input& input) return success; } +void DCCPlusPlusInterface::inputSimulateChange(uint32_t channel, uint32_t address) +{ + if(m_kernel && inRange(address, inputAddressMinMax(channel))) + m_kernel->simulateInputChange(address); +} + std::pair DCCPlusPlusInterface::outputAddressMinMax(uint32_t channel) const { using namespace DCCPlusPlus; diff --git a/server/src/hardware/interface/dccplusplusinterface.hpp b/server/src/hardware/interface/dccplusplusinterface.hpp index d6f1c1ca..acd5065f 100644 --- a/server/src/hardware/interface/dccplusplusinterface.hpp +++ b/server/src/hardware/interface/dccplusplusinterface.hpp @@ -84,6 +84,7 @@ class DCCPlusPlusInterface final std::pair inputAddressMinMax(uint32_t /*channel*/) const final { return {DCCPlusPlus::Kernel::idMin, DCCPlusPlus::Kernel::idMax}; } [[nodiscard]] bool addInput(Input& input) final; [[nodiscard]] bool removeInput(Input& input) final; + void inputSimulateChange(uint32_t channel, uint32_t address) final; // OutputController: const std::vector* outputChannels() const final { return &DCCPlusPlus::Kernel::outputChannels; } diff --git a/server/src/hardware/protocol/dccplusplus/iohandler/iohandler.hpp b/server/src/hardware/protocol/dccplusplus/iohandler/iohandler.hpp index 31748c44..8f4a661c 100644 --- a/server/src/hardware/protocol/dccplusplus/iohandler/iohandler.hpp +++ b/server/src/hardware/protocol/dccplusplus/iohandler/iohandler.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 @@ -52,6 +52,12 @@ class IOHandler virtual bool send(std::string_view message) = 0; }; +template +constexpr bool isSimulation() +{ + return false; +} + } #endif diff --git a/server/src/hardware/protocol/dccplusplus/iohandler/simulationiohandler.hpp b/server/src/hardware/protocol/dccplusplus/iohandler/simulationiohandler.hpp index c02c93ae..241da1a1 100644 --- a/server/src/hardware/protocol/dccplusplus/iohandler/simulationiohandler.hpp +++ b/server/src/hardware/protocol/dccplusplus/iohandler/simulationiohandler.hpp @@ -43,6 +43,12 @@ class SimulationIOHandler final : public IOHandler bool send(std::string_view message) final; }; +template<> +constexpr bool isSimulation() +{ + return true; +} + } #endif diff --git a/server/src/hardware/protocol/dccplusplus/kernel.cpp b/server/src/hardware/protocol/dccplusplus/kernel.cpp index 4c0c0b79..c150f1ad 100644 --- a/server/src/hardware/protocol/dccplusplus/kernel.cpp +++ b/server/src/hardware/protocol/dccplusplus/kernel.cpp @@ -36,8 +36,9 @@ namespace DCCPlusPlus { -Kernel::Kernel(const Config& config) +Kernel::Kernel(const Config& config, bool simulation) : m_ioContext{1} + , m_simulation{simulation} , m_startupDelayTimer{m_ioContext} , m_decoderController{nullptr} , m_inputController{nullptr} @@ -69,6 +70,7 @@ void Kernel::start() // reset all state values m_powerOn = TriState::Undefined; m_emergencyStop = TriState::Undefined; + m_inputValues.clear(); m_thread = std::thread( [this]() @@ -186,11 +188,18 @@ void Kernel::receive(std::string_view message) uint32_t id; if(auto r = fromChars(message.substr(3), id); r.ec == std::errc() && *r.ptr == '>' && id <= idMax) { - EventLoop::call( - [this, id, value=toTriState(message[1] == 'Q')]() - { - m_inputController->updateInputValue(InputController::defaultInputChannel, id, value); - }); + const bool value = message[1] == 'Q'; + auto it = m_inputValues.find(id); + if(it == m_inputValues.end() || it->second != value) + { + m_inputValues[id] = value; + + EventLoop::call( + [this, id, value]() + { + m_inputController->updateInputValue(InputController::defaultInputChannel, id, toTriState(value)); + }); + } } } break; @@ -329,6 +338,17 @@ bool Kernel::setOutput(uint32_t channel, uint16_t address, bool value) return false; } +void Kernel::simulateInputChange(uint16_t address) +{ + if(m_simulation) + m_ioContext.post( + [this, address]() + { + auto it = m_inputValues.find(address); + receive(Ex::sensorTransition(address, it != m_inputValues.end() ? !it->second : 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 add20b22..3256da2a 100644 --- a/server/src/hardware/protocol/dccplusplus/kernel.hpp +++ b/server/src/hardware/protocol/dccplusplus/kernel.hpp @@ -71,6 +71,7 @@ class Kernel private: boost::asio::io_context m_ioContext; std::unique_ptr m_ioHandler; + const bool m_simulation; std::thread m_thread; std::string m_logId; boost::asio::steady_timer m_startupDelayTimer; @@ -83,6 +84,7 @@ class Kernel DecoderController* m_decoderController; InputController* m_inputController; + std::unordered_map m_inputValues; OutputController* m_outputController; @@ -91,7 +93,7 @@ class Kernel bool m_started; #endif - Kernel(const Config& config); + Kernel(const Config& config, bool simulation); void setIOHandler(std::unique_ptr handler); @@ -130,7 +132,7 @@ class Kernel static std::unique_ptr create(const Config& config, Args... args) { static_assert(std::is_base_of_v); - std::unique_ptr kernel{new Kernel(config)}; + std::unique_ptr kernel{new Kernel(config, isSimulation())}; kernel->setIOHandler(std::make_unique(*kernel, std::forward(args)...)); return kernel; } @@ -289,6 +291,12 @@ class Kernel * @return \c true if send successful, \c false otherwise. */ bool setOutput(uint32_t channel, uint16_t address, bool value); + + /** + * \brief Simulate input change + * \param[in] address Input address, #idMin..#idMax + */ + void simulateInputChange(uint16_t address); }; } diff --git a/server/src/hardware/protocol/dccplusplus/messages.hpp b/server/src/hardware/protocol/dccplusplus/messages.hpp index e88e7d50..e2c2d812 100644 --- a/server/src/hardware/protocol/dccplusplus/messages.hpp +++ b/server/src/hardware/protocol/dccplusplus/messages.hpp @@ -283,6 +283,16 @@ namespace Ex { .append(">\n"); } + inline std::string sensorTransition(uint16_t id, bool active) + { + assert(id <= 32767); + + if(active) + return std::string("\n"); + else + return std::string("\n"); + } + inline std::string_view setSpeedSteps(uint8_t value) { switch(value)