ecos: added simulation of s88 objects

using data of last successful connection
Dieser Commit ist enthalten in:
Reinder Feenstra 2022-04-15 09:42:50 +02:00
Ursprung c68142278b
Commit 7afe5ea943
9 geänderte Dateien mit 211 neuen und 8 gelöschten Zeilen

Datei anzeigen

@ -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::SimulationIOHandler>(ecos->config());
m_kernel = ECoS::Kernel::create<ECoS::SimulationIOHandler>(ecos->config(), m_simulation);
else
m_kernel = ECoS::Kernel::create<ECoS::TCPIOHandler>(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);

Datei anzeigen

@ -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<ECoS::Kernel> 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;

Datei anzeigen

@ -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("<END 0 (OK)>\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("<END 0 (OK)>\r\n");
return reply(response);
}
return reply(std::string("<REPLY ").append(message).append(">\r\n<END 999 (Traintastic: no simulation support)>\r\n"));
}
@ -108,7 +138,12 @@ bool SimulationIOHandler::reply(std::string_view message)
bool SimulationIOHandler::replyOk(std::string_view request)
{
return reply(std::string("<REPLY ").append(rtrim(request, {'\r', '\n'})).append(">\r\n<END 0 (OK)>\r\n"));
return reply(replyHeader(request).append("<END 0 (OK)>\r\n"));
}
std::string SimulationIOHandler::replyHeader(std::string_view request)
{
return std::string("<REPLY ").append(rtrim(request, {'\r', '\n'})).append(">\r\n");
}
}

Datei anzeigen

@ -26,17 +26,22 @@
#include "iohandler.hpp"
#include <array>
#include <cstddef>
#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 {}

Datei anzeigen

@ -23,6 +23,7 @@
#include "kernel.hpp"
#include <algorithm>
#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<Feedback*>(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

Datei anzeigen

@ -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 ...

Datei anzeigen

@ -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<uint8_t>(m_state.size()); }
};
}

Datei anzeigen

@ -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 <vector>
#include <cstdint>
namespace ECoS {
struct Simulation
{
struct Object
{
uint16_t id;
};
struct S88 : Object
{
uint8_t ports;
};
std::vector<S88> s88;
void clear()
{
s88.clear();
}
};
}
#endif

Datei anzeigen

@ -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 <vector>
template<class T>
inline bool contains(const std::vector<T>& vector, T value)
{
return std::find(vector.begin(), vector.end(), value) != vector.end();
}
#endif