/** * server/src/hardware/protocol/ecos/kernel.cpp * * This file is part of the traintastic source code. * * Copyright (C) 2021 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. */ #include "kernel.hpp" #include #include "messages.hpp" #include "object/ecos.hpp" #include "object/locomotivemanager.hpp" #include "object/switchmanager.hpp" #include "object/feedbackmanager.hpp" #include "../../decoder/decoder.hpp" #include "../../decoder/decoderchangeflags.hpp" #include "../../../utils/setthreadname.hpp" #include "../../../utils/startswith.hpp" #include "../../../utils/rtrim.hpp" #include "../../../core/eventloop.hpp" #include "../../../log/log.hpp" namespace ECoS { Kernel::Kernel(const Config& config) : m_ioContext{1} , m_decoderController{nullptr} , m_inputController{nullptr} , m_outputController{nullptr} , m_config{config} #ifndef NDEBUG , m_started{false} #endif { } void Kernel::setConfig(const Config& config) { m_ioContext.post( [this, newConfig=config]() { m_config = newConfig; }); } void Kernel::setOnStarted(std::function callback) { assert(!m_started); m_onStarted = std::move(callback); } void Kernel::setOnEmergencyStop(std::function callback) { assert(!m_started); m_onEmergencyStop = std::move(callback); } void Kernel::setOnGo(std::function callback) { assert(!m_started); m_onGo = std::move(callback); } void Kernel::setDecoderController(DecoderController* decoderController) { assert(!m_started); m_decoderController = decoderController; } void Kernel::setInputController(InputController* inputController) { assert(!m_started); m_inputController = inputController; } void Kernel::setOutputController(OutputController* outputController) { assert(!m_started); m_outputController = outputController; } void Kernel::start() { assert(m_ioHandler); assert(!m_started); assert(m_objects.empty()); m_thread = std::thread( [this]() { setThreadName("ecos"); auto work = std::make_shared(m_ioContext); m_ioContext.run(); }); m_ioContext.post( [this]() { m_ioHandler->start(); m_objects.add(std::make_unique(*this)); m_objects.add(std::make_unique(*this)); m_objects.add(std::make_unique(*this)); m_objects.add(std::make_unique(*this)); if(m_onStarted) EventLoop::call( [this]() { m_onStarted(); }); }); #ifndef NDEBUG m_started = true; #endif } void Kernel::stop() { m_ioContext.post( [this]() { m_ioHandler->stop(); }); m_ioContext.stop(); m_thread.join(); m_objects.clear(); #ifndef NDEBUG m_started = false; #endif } void Kernel::receive(std::string_view message) { if(m_config.debugLogRXTX) { std::string msg{rtrim(message, {'\r', '\n'})}; std::replace_if(msg.begin(), msg.end(), [](char c){ return c == '\r' || c == '\n'; }, ';'); EventLoop::call([this, msg](){ Log::log(m_logId, LogMessage::D2002_RX_X, msg); }); } if(Reply reply; parseReply(message, reply)) { auto it = m_objects.find(reply.objectId); if(it != m_objects.end()) it->second->receiveReply(reply); } else if(Event event; parseEvent(message, event)) { auto it = m_objects.find(event.objectId); if(it != m_objects.end()) it->second->receiveEvent(event); } else {}// EventLoop::call([this]() { Log::log(m_logId, LogMessage::E2018_ParseError); }); } ECoS& Kernel::ecos() { return static_cast(*m_objects[ObjectId::ecos]); } void Kernel::ecosGoChanged(TriState value) { if(value == TriState::False && m_onEmergencyStop) EventLoop::call([this]() { m_onEmergencyStop(); }); else if(value == TriState::True && m_onGo) EventLoop::call([this]() { m_onGo(); }); } void Kernel::emergencyStop() { m_ioContext.post([this]() { ecos().stop(); }); } void Kernel::go() { m_ioContext.post([this]() { ecos().go(); }); } void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes, uint32_t functionNumber) { (void)(decoder); (void)(changes); (void)(functionNumber); } bool Kernel::setOutput(uint16_t address, bool value) { (void)(address); (void)(value); return false; } void Kernel::setIOHandler(std::unique_ptr handler) { assert(handler); assert(!m_ioHandler); m_ioHandler = std::move(handler); } void Kernel::send(std::string_view message) { if(m_ioHandler->send(message)) { if(m_config.debugLogRXTX) EventLoop::call( [this, msg=std::string(rtrim(message, '\n'))]() { Log::log(m_logId, LogMessage::D2001_TX_X, msg); }); } else {} // log message and go to error state } }