ecos: implemented switch by address commands

Dieser Commit ist enthalten in:
Reinder Feenstra 2022-04-03 19:22:04 +02:00
Ursprung 4bf74802f1
Commit ee5de7705b
11 geänderte Dateien mit 261 neuen und 39 gelöschten Zeilen

Datei anzeigen

@ -33,7 +33,7 @@
constexpr auto decoderListColumns = DecoderListColumn::Id | DecoderListColumn::Name | DecoderListColumn::Address;
constexpr auto inputListColumns = InputListColumn::Id | InputListColumn::Name | InputListColumn::Channel | InputListColumn::Address;
constexpr auto outputListColumns = OutputListColumn::Id | OutputListColumn::Name | OutputListColumn::Address;
constexpr auto outputListColumns = OutputListColumn::Id | OutputListColumn::Name | OutputListColumn::Channel | OutputListColumn::Address;
ECoSInterface::ECoSInterface(World& world, std::string_view _id)
: Interface(world, _id)
@ -130,6 +130,23 @@ bool ECoSInterface::removeInput(Input& input)
return success;
}
std::pair<uint32_t, uint32_t> ECoSInterface::outputAddressMinMax(uint32_t channel) const
{
using namespace ECoS;
switch(channel)
{
case Kernel::OutputChannel::dcc:
return {Kernel::outputDCCAddressMin, Kernel::outputDCCAddressMax};
case Kernel::OutputChannel::motorola:
return {Kernel::outputMotorolaAddressMin, Kernel::outputMotorolaAddressMax};
}
assert(false);
return {0, 0};
}
bool ECoSInterface::addOutput(Output& output)
{
const bool success = OutputController::addOutput(output);
@ -151,7 +168,7 @@ bool ECoSInterface::setOutputValue(uint32_t channel, uint32_t address, bool valu
return
m_kernel &&
inRange(address, outputAddressMinMax(channel)) &&
m_kernel->setOutput(static_cast<uint16_t>(address), value);
m_kernel->setOutput(channel, static_cast<uint16_t>(address), value);
}
bool ECoSInterface::setOnline(bool& value, bool simulation)

Datei anzeigen

@ -86,7 +86,9 @@ class ECoSInterface final
[[nodiscard]] bool removeInput(Input& input) final;
// OutputController:
std::pair<uint32_t, uint32_t> outputAddressMinMax(uint32_t /*channel*/) const final { return {1, 1}; }
const std::vector<uint32_t>* outputChannels() const final { return &ECoS::Kernel::outputChannels; }
const std::vector<std::string_view>* outputChannelNames() const final { return &ECoS::Kernel::outputChannelNames; }
std::pair<uint32_t, uint32_t> outputAddressMinMax(uint32_t channel) const final;
[[nodiscard]] bool addOutput(Output& output) final;
[[nodiscard]] bool removeOutput(Output& output) final;
[[nodiscard]] bool setOutputValue(uint32_t channel, uint32_t address, bool value) final;

Datei anzeigen

@ -32,6 +32,7 @@
#include "../../decoder/decoder.hpp"
#include "../../decoder/decoderchangeflags.hpp"
#include "../../input/inputcontroller.hpp"
#include "../../output/outputcontroller.hpp"
#include "../../../utils/setthreadname.hpp"
#include "../../../utils/startswith.hpp"
#include "../../../utils/rtrim.hpp"
@ -244,6 +245,13 @@ Locomotive* Kernel::getLocomotive(DecoderProtocol protocol, uint16_t address, ui
return nullptr;
}
SwitchManager& Kernel::switchManager()
{
ASSERT_IS_KERNEL_THREAD;
return static_cast<SwitchManager&>(*m_objects[ObjectId::switchManager]);
}
void Kernel::emergencyStop()
{
m_ioContext.post([this]() { ecos().stop(); });
@ -304,14 +312,67 @@ void Kernel::decoderChanged(const Decoder& decoder, DecoderChangeFlags changes,
}
}
bool Kernel::setOutput(uint16_t address, bool value)
bool Kernel::setOutput(uint32_t channel, uint16_t address, bool value)
{
(void)(address);
(void)(value);
if(value)
{
switch(channel)
{
case OutputChannel::dcc:
m_ioContext.post(
[this, address]()
{
switchManager().setSwitch(SwitchProtocol::DCC, address);
});
return true;
case OutputChannel::motorola:
m_ioContext.post(
[this, address]()
{
switchManager().setSwitch(SwitchProtocol::Motorola, address);
});
return true;
}
assert(false);
}
return false;
}
void Kernel::switchManagerSwitched(SwitchProtocol protocol, uint16_t address)
{
ASSERT_IS_KERNEL_THREAD;
if(!m_outputController)
return;
switch(protocol)
{
case SwitchProtocol::DCC:
EventLoop::call(
[this, address]()
{
m_outputController->updateOutputValue(OutputChannel::dcc, address, TriState::True);
m_outputController->updateOutputValue(OutputChannel::dcc, (address & 1) ? (address + 1) : (address - 1), TriState::False);
});
break;
case SwitchProtocol::Motorola:
EventLoop::call(
[this, address]()
{
m_outputController->updateOutputValue(OutputChannel::motorola, address, TriState::True);
m_outputController->updateOutputValue(OutputChannel::motorola, (address & 1) ? (address + 1) : (address - 1), TriState::False);
});
break;
case SwitchProtocol::Unknown:
assert(false);
break;
}
}
void Kernel::feedbackStateChanged(Feedback& object, uint8_t port, TriState value)
{
if(!m_inputController)

Datei anzeigen

@ -30,6 +30,7 @@
#include "config.hpp"
#include "iohandler/iohandler.hpp"
#include "object/object.hpp"
#include "object/switchprotocol.hpp"
class Decoder;
enum class DecoderChangeFlags;
@ -41,6 +42,7 @@ namespace ECoS {
class ECoS;
class Locomotive;
class SwitchManager;
class Feedback;
class Kernel
@ -70,6 +72,27 @@ class Kernel
"$ecos_channel:ecos_detector$",
};
static constexpr uint16_t outputDCCAddressMin = 1;
static constexpr uint16_t outputDCCAddressMax = 1000; //!< \todo what is the maximum
static constexpr uint16_t outputMotorolaAddressMin = 1;
static constexpr uint16_t outputMotorolaAddressMax = 1000; //!< \todo what is the maximum
struct OutputChannel
{
static constexpr uint32_t dcc = 1;
static constexpr uint32_t motorola = 2;
};
inline static const std::vector<uint32_t> outputChannels = {
OutputChannel::dcc,
OutputChannel::motorola,
};
inline static const std::vector<std::string_view> outputChannelNames = {
"$hardware:dcc$",
"$hardware:motorola$",
};
private:
class Objects : public std::unordered_map<uint16_t, std::unique_ptr<Object>>
{
@ -111,6 +134,8 @@ class Kernel
Locomotive* getLocomotive(DecoderProtocol protocol, uint16_t address, uint8_t speedSteps);
SwitchManager& switchManager();
public:// REMOVE!! just for testing
void postSend(const std::string& message)
{
@ -263,11 +288,14 @@ class Kernel
/**
* @brief ...
* @param[in] channel Channel
* @param[in] address Output address
* @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);
bool setOutput(uint32_t channel, uint16_t address, bool value);
void switchManagerSwitched(SwitchProtocol protocol, uint16_t address);
void feedbackStateChanged(Feedback& object, uint8_t port, TriState value);
};

Datei anzeigen

@ -77,6 +77,7 @@ struct Option
static constexpr std::string_view state = "state";
static constexpr std::string_view status = "status";
static constexpr std::string_view stop = "stop";
static constexpr std::string_view switch_ = "switch";
static constexpr std::string_view view = "view";
};
@ -135,7 +136,12 @@ inline std::string set(uint16_t objectId, std::string_view option, T value)
std::string s(Command::set);
s.append("(").append(std::to_string(objectId));
s.append(", ").append(option);
s.append("[").append(std::to_string(value)).append("]");
s.append("[");
if constexpr(std::is_same_v<T, std::string>)
s.append(value);
else
s.append(std::to_string(value));
s.append("]");
s.append(")\n");
return s;
}

Datei anzeigen

@ -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
@ -27,18 +27,7 @@
namespace ECoS {
const std::initializer_list<std::string_view> Switch::options = {Option::addr, Option::protocol, Option::state, Option::mode, Option::duration};
static bool fromString(std::string_view text, Switch::Protocol& protocol)
{
if(text == "MM")
protocol = Switch::Protocol::MM;
else if(text == "DCC")
protocol = Switch::Protocol::DCC;
else
return false;
return true;
}
const std::initializer_list<std::string_view> Switch::options = {Option::addr, Option::protocol};
static bool fromString(std::string_view text, Switch::Mode& mode)
{
@ -55,6 +44,7 @@ Switch::Switch(Kernel& kernel, uint16_t id)
: Object(kernel, id)
{
requestView();
send(get(m_id, {Option::state, Option::mode, Option::duration}));
}
Switch::Switch(Kernel& kernel, const Line& data)
@ -65,12 +55,6 @@ Switch::Switch(Kernel& kernel, const Line& data)
fromChars(addr->second, m_address);
if(auto protocol = values.find(Option::protocol); protocol != values.end())
fromString(protocol->second, m_protocol);
if(auto state = values.find(Option::state); state != values.end())
{}
if(auto mode = values.find(Option::mode); mode != values.end())
fromString(mode->second, m_mode);
if(auto duration = values.find(Option::duration); duration != values.end())
fromChars(duration->second, m_duration);
}
bool Switch::receiveReply(const Reply& reply)
@ -91,4 +75,20 @@ bool Switch::receiveEvent(const Event& event)
return Object::receiveEvent(event);
}
void Switch::update(std::string_view option, std::string_view value)
{
if(option == Option::state)
{
(void)value; //! \todo implement
}
else if(option == Option::mode)
{
fromString(value, m_mode);
}
else if(option == Option::duration)
{
(void)value; //! \todo implement
}
}
}

Datei anzeigen

@ -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
@ -24,6 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_ECOS_OBJECT_SWITCH_HPP
#include "object.hpp"
#include "switchprotocol.hpp"
#include "../messages.hpp"
namespace ECoS {
@ -34,13 +35,6 @@ struct Line;
class Switch final : public Object
{
public:
enum class Protocol
{
Unknown = 0,
DCC = 1,
MM = 2,
};
enum class Mode
{
Unknown = 0,
@ -50,10 +44,13 @@ class Switch final : public Object
private:
uint16_t m_address = 0;
Protocol m_protocol = Protocol::Unknown;
SwitchProtocol m_protocol = SwitchProtocol::Unknown;
Mode m_mode = Mode::Unknown;
uint16_t m_duration = 0;
protected:
void update(std::string_view option, std::string_view value) final;
public:
static const std::initializer_list<std::string_view> options;
@ -64,7 +61,7 @@ class Switch final : public Object
bool receiveEvent(const Event& event) final;
uint16_t address() const { return m_address; }
Protocol protocol() const { return m_protocol; }
SwitchProtocol protocol() const { return m_protocol; }
Mode mode() const { return m_mode; }
uint16_t duration() const { return m_duration; }
};

Datei anzeigen

@ -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
@ -23,7 +23,10 @@
#include "switchmanager.hpp"
#include <cassert>
#include "switch.hpp"
#include "../kernel.hpp"
#include "../messages.hpp"
#include "../../../../utils/fromchars.hpp"
#include "../../../../utils/startswith.hpp"
namespace ECoS {
@ -34,6 +37,12 @@ SwitchManager::SwitchManager(Kernel& kernel)
send(queryObjects(m_id, Switch::options));
}
void SwitchManager::setSwitch(SwitchProtocol protocol, uint16_t address)
{
if(protocol == SwitchProtocol::DCC || protocol == SwitchProtocol::Motorola)
send(set(m_id, Option::switch_, std::string((protocol == SwitchProtocol::Motorola) ? "MOT" : "DCC").append(std::to_string(1 + ((address - 1) >> 1))).append(((address - 1) & 1) ? "g" : "r")));
}
bool SwitchManager::receiveReply(const Reply& reply)
{
assert(reply.objectId == m_id);
@ -60,4 +69,29 @@ bool SwitchManager::receiveEvent(const Event& event)
return Object::receiveEvent(event);
}
void SwitchManager::update(std::string_view option, std::string_view value)
{
if(option == Option::switch_)
{
auto protocol = SwitchProtocol::Unknown;
for(auto p : {SwitchProtocol::DCC, SwitchProtocol::Motorola})
if(startsWith(value, toString(p)))
{
protocol = p;
value = value.substr(toString(p).size());
break;
}
if(protocol != SwitchProtocol::Unknown)
{
uint16_t address;
if(auto r = fromChars(value, address); r.ec == std::errc() && r.ptr < value.data() + value.size())
{
address = (address << 1) - ((*r.ptr == 'r') ? 1 : 0);
m_kernel.switchManagerSwitched(protocol, address);
}
}
}
}
}

Datei anzeigen

@ -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
@ -24,6 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_ECOS_OBJECT_SWITCHMANAGER_HPP
#include "object.hpp"
#include "switchprotocol.hpp"
namespace ECoS {
@ -31,9 +32,14 @@ class Kernel;
class SwitchManager final : public Object
{
protected:
void update(std::string_view option, std::string_view value) final;
public:
SwitchManager(Kernel& kernel);
void setSwitch(SwitchProtocol protocol, uint16_t address);
bool receiveReply(const Reply& reply) final;
bool receiveEvent(const Event& event) final;
};

Datei anzeigen

@ -0,0 +1,67 @@
/**
* server/src/hardware/protocol/ecos/object/switchprotocol.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_OBJECT_SWITCHPROTOCOL_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_ECOS_OBJECT_SWITCHPROTOCOL_HPP
#include <string_view>
namespace ECoS
{
enum class SwitchProtocol
{
Unknown = 0,
DCC = 1,
Motorola = 2,
};
constexpr std::string_view toString(SwitchProtocol value)
{
switch(value)
{
case SwitchProtocol::DCC:
return "DCC";
case SwitchProtocol::Motorola:
return "MM";
case SwitchProtocol::Unknown:
break;
}
return {};
}
constexpr bool fromString(std::string_view text, SwitchProtocol& protocol)
{
if(text == "MM")
protocol = SwitchProtocol::Motorola;
else if(text == "DCC")
protocol = SwitchProtocol::DCC;
else
return false;
return true;
}
}
#endif

Datei anzeigen

@ -134,6 +134,7 @@ ecos_channel:ecos_detector=ECoS Detector
hardware:address=Address
hardware:channel=Channel
hardware:command_station=Command station
hardware:dcc=DCC
hardware:dccplusplus=DCC++
hardware:debug_log_input=Log input changes
hardware:debug_log_output=Log output changes
@ -144,6 +145,7 @@ hardware:input_monitor=Input monitor
hardware:inputs=Inputs
hardware:interface=Interface
hardware:loconet=LocoNet
hardware:motorola=Motorola
hardware:output_keyboard=Output keyboard
hardware:outputs=Outputs
hardware:s88=S88
@ -159,6 +161,8 @@ input_map_item.block:type=Type
interface.dccplusplus:dcc_plus_plus=DCC++(EX)
interface.ecos:ecos=ECoS
interface.loconet:interface=Interface
interface.xpressnet:interface=Interface