From 7afe5ea943a675aea862b82d48ca4aca27e33ca1 Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Fri, 15 Apr 2022 09:42:50 +0200 Subject: [PATCH] ecos: added simulation of s88 objects using data of last successful connection --- .../src/hardware/interface/ecosinterface.cpp | 52 +++++++++++++++++- .../src/hardware/interface/ecosinterface.hpp | 4 ++ .../ecos/iohandler/simulationiohandler.cpp | 41 ++++++++++++-- .../ecos/iohandler/simulationiohandler.hpp | 7 ++- server/src/hardware/protocol/ecos/kernel.cpp | 22 +++++++- server/src/hardware/protocol/ecos/kernel.hpp | 4 +- .../protocol/ecos/object/feedback.hpp | 2 + .../src/hardware/protocol/ecos/simulation.hpp | 53 +++++++++++++++++++ server/src/utils/contains.hpp | 34 ++++++++++++ 9 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 server/src/hardware/protocol/ecos/simulation.hpp create mode 100644 server/src/utils/contains.hpp diff --git a/server/src/hardware/interface/ecosinterface.cpp b/server/src/hardware/interface/ecosinterface.cpp index c643f80e..bc9bd1db 100644 --- a/server/src/hardware/interface/ecosinterface.cpp +++ b/server/src/hardware/interface/ecosinterface.cpp @@ -31,6 +31,7 @@ #include "../../utils/displayname.hpp" #include "../../utils/inrange.hpp" #include "../../world/world.hpp" +#include "../../world/worldloader.hpp" constexpr auto decoderListColumns = DecoderListColumn::Id | DecoderListColumn::Name | DecoderListColumn::Address; constexpr auto inputListColumns = InputListColumn::Id | InputListColumn::Name | InputListColumn::Channel | InputListColumn::Address; @@ -179,7 +180,7 @@ bool ECoSInterface::setOnline(bool& value, bool simulation) try { if(simulation) - m_kernel = ECoS::Kernel::create(ecos->config()); + m_kernel = ECoS::Kernel::create(ecos->config(), m_simulation); else m_kernel = ECoS::Kernel::create(ecos->config(), hostname.value()); @@ -234,7 +235,7 @@ bool ECoSInterface::setOnline(bool& value, bool simulation) m_ecosPropertyChanged.disconnect(); - m_kernel->stop(); + m_kernel->stop(simulation ? nullptr : &m_simulation); m_kernel.reset(); status.setValueInternal(InterfaceStatus::Offline); @@ -278,6 +279,53 @@ void ECoSInterface::destroying() Interface::destroying(); } +void ECoSInterface::load(WorldLoader& loader, const nlohmann::json& data) +{ + Interface::load(loader, data); + + using nlohmann::json; + + json state = loader.getState(getObjectId()); + // load simulation data: + if(json simulation = state.value("simulation", json::object()); !simulation.empty()) + { + using ECoS::Simulation; + + if(json s88 = simulation.value("s88", json::array()); !s88.empty()) + { + for(const json& object : s88) + { + const uint16_t objectId = object.value("id", 0U); + const uint8_t ports = object.value("ports", 0U); + if(objectId != 0 && (ports == 8 || ports == 16)) + m_simulation.s88.emplace_back(Simulation::S88{{objectId}, ports}); + else + break; + } + } + } +} + +void ECoSInterface::save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& state) const +{ + Interface::save(saver, data, state); + + using nlohmann::json; + + // save data for simulation: + json simulation = json::object(); + if(!m_simulation.s88.empty()) + { + json objects = json::array(); + for(const auto& s88 : m_simulation.s88) + objects.emplace_back(json::object({{"id", s88.id}, {"ports", s88.ports}})); + simulation["s88"] = objects; + } + + if(!simulation.empty()) + state["simulation"] = simulation; +} + void ECoSInterface::worldEvent(WorldState state, WorldEvent event) { Interface::worldEvent(state, event); diff --git a/server/src/hardware/interface/ecosinterface.hpp b/server/src/hardware/interface/ecosinterface.hpp index 80081e67..85ebca41 100644 --- a/server/src/hardware/interface/ecosinterface.hpp +++ b/server/src/hardware/interface/ecosinterface.hpp @@ -26,6 +26,7 @@ #include "interface.hpp" #include "../protocol/ecos/kernel.hpp" #include "../protocol/ecos/settings.hpp" +#include "../protocol/ecos/simulation.hpp" #include "../decoder/decodercontroller.hpp" #include "../decoder/list/decoderlist.hpp" #include "../input/inputcontroller.hpp" @@ -50,9 +51,12 @@ class ECoSInterface final private: std::unique_ptr m_kernel; boost::signals2::connection m_ecosPropertyChanged; + ECoS::Simulation m_simulation; void addToWorld() final; void destroying() final; + void load(WorldLoader& loader, const nlohmann::json& data) final; + void save(WorldSaver& saver, nlohmann::json& data, nlohmann::json& state) const final; void worldEvent(WorldState state, WorldEvent event) final; void idChanged(const std::string& newId) final; diff --git a/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.cpp b/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.cpp index f145138b..b9aed779 100644 --- a/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.cpp +++ b/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.cpp @@ -24,11 +24,13 @@ #include "../kernel.hpp" #include "../messages.hpp" #include "../../../../utils/rtrim.hpp" +#include "../../../../utils/contains.hpp" namespace ECoS { -SimulationIOHandler::SimulationIOHandler(Kernel& kernel) +SimulationIOHandler::SimulationIOHandler(Kernel& kernel, const Simulation& simulation) : IOHandler(kernel) + , m_simulation{simulation} { } @@ -87,9 +89,37 @@ bool SimulationIOHandler::send(std::string_view message) { if(request.command == Command::queryObjects) { - return replyOk(message); // empty list for now + const bool ports = contains(request.options, Option::ports); + + std::string response{replyHeader(message)}; + for(const auto& s88 : m_simulation.s88) + { + response.append(std::to_string(s88.id)); + if(ports) + response.append(" ports[").append(std::to_string(s88.ports)).append("]"); + response.append("\r\n"); + } + response.append("\r\n"); + + return reply(response); } } + else if(auto it = std::find_if(m_simulation.s88.begin(), m_simulation.s88.end(), + [id=request.objectId](const auto& v) + { + return v.id == id; + }); it != m_simulation.s88.end()) + { + const bool state = contains(request.options, Option::state); + + std::string response{replyHeader(message)}; + response.append(std::to_string(request.objectId)); + if(state) + response.append(" state[0x0]"); + response.append("\r\n"); + + return reply(response); + } return reply(std::string("\r\n\r\n")); } @@ -108,7 +138,12 @@ bool SimulationIOHandler::reply(std::string_view message) bool SimulationIOHandler::replyOk(std::string_view request) { - return reply(std::string("\r\n\r\n")); + return reply(replyHeader(request).append("\r\n")); +} + +std::string SimulationIOHandler::replyHeader(std::string_view request) +{ + return std::string("\r\n"); } } diff --git a/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.hpp b/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.hpp index 3d62ce17..a1d314db 100644 --- a/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.hpp +++ b/server/src/hardware/protocol/ecos/iohandler/simulationiohandler.hpp @@ -26,17 +26,22 @@ #include "iohandler.hpp" #include #include +#include "../simulation.hpp" namespace ECoS { class SimulationIOHandler final : public IOHandler { private: + const Simulation m_simulation; + bool reply(std::string_view message); bool replyOk(std::string_view request); + static std::string replyHeader(std::string_view request); + public: - SimulationIOHandler(Kernel& kernel); + SimulationIOHandler(Kernel& kernel, const Simulation& simulation); void start() final {} void stop() final {} diff --git a/server/src/hardware/protocol/ecos/kernel.cpp b/server/src/hardware/protocol/ecos/kernel.cpp index f2305f7c..fb848c57 100644 --- a/server/src/hardware/protocol/ecos/kernel.cpp +++ b/server/src/hardware/protocol/ecos/kernel.cpp @@ -23,6 +23,7 @@ #include "kernel.hpp" #include #include "messages.hpp" +#include "simulation.hpp" #include "object/ecos.hpp" #include "object/locomotivemanager.hpp" #include "object/locomotive.hpp" @@ -161,7 +162,7 @@ void Kernel::start() #endif } -void Kernel::stop() +void Kernel::stop(Simulation* simulation) { m_ioContext.post( [this]() @@ -173,6 +174,25 @@ void Kernel::stop() m_thread.join(); + if(simulation) // get simulation data + { + simulation->clear(); + + // S88: + { + uint16_t id = ObjectId::s88; + auto it = m_objects.find(id); + while(it != m_objects.end()) + { + if(const auto* feedback = dynamic_cast(it->second.get())) + simulation->s88.emplace_back(Simulation::S88{{feedback->id()}, feedback->ports()}); + else + break; + it = m_objects.find(++id); + } + } + } + m_objects.clear(); #ifndef NDEBUG diff --git a/server/src/hardware/protocol/ecos/kernel.hpp b/server/src/hardware/protocol/ecos/kernel.hpp index 15c50c77..5e003c7f 100644 --- a/server/src/hardware/protocol/ecos/kernel.hpp +++ b/server/src/hardware/protocol/ecos/kernel.hpp @@ -44,6 +44,7 @@ class ECoS; class Locomotive; class SwitchManager; class Feedback; +struct Simulation; class Kernel { @@ -255,8 +256,9 @@ class Kernel /** * @brief Stop the kernel and IO handler + * @param[out] simulation Get simulation data (optional) */ - void stop(); + void stop(Simulation* simulation); /** * @brief ... diff --git a/server/src/hardware/protocol/ecos/object/feedback.hpp b/server/src/hardware/protocol/ecos/object/feedback.hpp index 82add774..c8ce447a 100644 --- a/server/src/hardware/protocol/ecos/object/feedback.hpp +++ b/server/src/hardware/protocol/ecos/object/feedback.hpp @@ -45,6 +45,8 @@ class Feedback final : public Object Feedback(Kernel& kernel, uint16_t id); Feedback(Kernel& kernel, const Line& data); + + uint8_t ports() const { return static_cast(m_state.size()); } }; } diff --git a/server/src/hardware/protocol/ecos/simulation.hpp b/server/src/hardware/protocol/ecos/simulation.hpp new file mode 100644 index 00000000..f3058322 --- /dev/null +++ b/server/src/hardware/protocol/ecos/simulation.hpp @@ -0,0 +1,53 @@ +/** + * server/src/hardware/protocol/ecos/simulation.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_ECOS_SIMULATION_HPP +#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_ECOS_SIMULATION_HPP + +#include +#include + +namespace ECoS { + +struct Simulation +{ + struct Object + { + uint16_t id; + }; + + struct S88 : Object + { + uint8_t ports; + }; + + std::vector s88; + + void clear() + { + s88.clear(); + } +}; + +} + +#endif diff --git a/server/src/utils/contains.hpp b/server/src/utils/contains.hpp new file mode 100644 index 00000000..6f761860 --- /dev/null +++ b/server/src/utils/contains.hpp @@ -0,0 +1,34 @@ +/** + * server/src/utils/contains.hpp + * + * This file is part of the traintastic source code. + * + * Copyright (C) 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 + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef TRAINTASTIC_SERVER_UTILS_CONTAINS_HPP +#define TRAINTASTIC_SERVER_UTILS_CONTAINS_HPP + +#include + +template +inline bool contains(const std::vector& vector, T value) +{ + return std::find(vector.begin(), vector.end(), value) != vector.end(); +} + +#endif