loconet: split into multiple files, added F20/F28 message (untested)

Dieser Commit ist enthalten in:
Reinder Feenstra 2020-07-20 23:42:01 +02:00
Ursprung 4bb36509a5
Commit 6c8decaafa
14 geänderte Dateien mit 1811 neuen und 1347 gelöschten Zeilen

Datei anzeigen

@ -35,6 +35,8 @@ file(GLOB SOURCES
"src/hardware/input/*.cpp"
"src/hardware/protocol/*.hpp"
"src/hardware/protocol/*.cpp"
"src/hardware/protocol/loconet/*.hpp"
"src/hardware/protocol/loconet/*.cpp"
"src/utils/*.hpp"
"src/utils/*.cpp"
"thirdparty/boost/libs/program_options/src/*.cpp")

Datei anzeigen

@ -25,6 +25,8 @@
#include "../../core/traintastic.hpp"
#include "../../core/eventloop.hpp"
using namespace Protocol::LocoNet;
namespace Hardware::CommandStation {
LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id) :
@ -64,7 +66,7 @@ LocoNetSerial::LocoNetSerial(const std::weak_ptr<World>& world, std::string_view
loconet{this, "loconet", nullptr, PropertyFlags::ReadOnly | PropertyFlags::Store | PropertyFlags::SubObject}
{
name = "LocoNet (serial)";
loconet.setValueInternal(std::make_shared<::Protocol::LocoNet>(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
loconet.setValueInternal(std::make_shared<LocoNet>(*this, loconet.name(), std::bind(&LocoNetSerial::send, this, std::placeholders::_1)));
port.addAttributeEnabled(!online);
interface.addAttributeEnabled(!online);
@ -103,9 +105,9 @@ void LocoNetSerial::emergencyStopChanged(bool value)
CommandStation::emergencyStopChanged(value);
if(value)
send(Protocol::LocoNet::Idle());
send(Idle());
else if(!trackVoltageOff)
send(Protocol::LocoNet::GlobalPowerOn());
send(GlobalPowerOn());
}
void LocoNetSerial::trackVoltageOffChanged(bool value)
@ -113,9 +115,9 @@ void LocoNetSerial::trackVoltageOffChanged(bool value)
CommandStation::trackVoltageOffChanged(value);
if(!value)
send(Protocol::LocoNet::GlobalPowerOn());
send(GlobalPowerOn());
else
send(Protocol::LocoNet::GlobalPowerOff());
send(GlobalPowerOff());
}
void LocoNetSerial::decoderChanged(const Hardware::Decoder& decoder, Hardware::DecoderChangeFlags changes, uint32_t functionNumber)

Datei anzeigen

@ -24,7 +24,7 @@
#define TRAINTASTIC_SERVER_HARDWARE_COMMANDSTATION_LOCONETSERIAL_HPP
#include "commandstation.hpp"
#include "../protocol/loconet.hpp"
#include "../protocol/loconet/loconet.hpp"
#include "../../enum/loconetserialinterface.hpp"
#include "../../enum/serialflowcontrol.hpp"
#include <boost/asio/serial_port.hpp>
@ -56,7 +56,7 @@ class LocoNetSerial : public CommandStation
Property<LocoNetSerialInterface> interface;
Property<uint32_t> baudrate;
Property<SerialFlowControl> flowControl;
ObjectProperty<::Protocol::LocoNet> loconet;
ObjectProperty<::Protocol::LocoNet::LocoNet> loconet;
LocoNetSerial(const std::weak_ptr<World>& world, std::string_view _id);
};

Datei anzeigen

@ -82,7 +82,7 @@ Z21::Z21(const std::weak_ptr<World>& world, std::string_view _id) :
shortCircutExternal{this, "short_circut_external", false, PropertyFlags::ReadOnly}
{
name = "Z21";
loconet.setValueInternal(std::make_shared<::Protocol::LocoNet>(*this, loconet.name(),
loconet.setValueInternal(std::make_shared<::Protocol::LocoNet::LocoNet>(*this, loconet.name(),
[/*this*/](const ::Protocol::LocoNet::Message& /*msg*/)
{
return false;

Datei anzeigen

@ -27,7 +27,7 @@
#include <boost/asio.hpp>
#include "../../core/objectproperty.hpp"
//#include "protocol/xpressnet.hpp"
#include "../protocol/loconet.hpp"
#include "../protocol/loconet/loconet.hpp"
struct z21_lan_header;
@ -64,7 +64,7 @@ class Z21 : public CommandStation
Property<std::string> hostname;
Property<uint16_t> port;
ObjectProperty<::Protocol::LocoNet> loconet;
ObjectProperty<::Protocol::LocoNet::LocoNet> loconet;
Property<std::string> serialNumber;
Property<std::string> hardwareType;
Property<std::string> firmwareVersion;

Datei anzeigen

@ -22,10 +22,12 @@
#include "loconetinput.hpp"
using namespace Protocol::LocoNet;
LocoNetInput::LocoNetInput(const std::weak_ptr<World> world, std::string_view _id) :
Input(world, _id),
loconet{this, "loconet", nullptr, PropertyFlags::ReadWrite | PropertyFlags::Store,
[this](const std::shared_ptr<Protocol::LocoNet>& value)
[this](const std::shared_ptr<LocoNet>& value)
{
if(!value || value->addInput(shared_ptr<LocoNetInput>()))
{

Datei anzeigen

@ -25,11 +25,11 @@
#include "input.hpp"
#include "../../core/objectproperty.hpp"
#include "../protocol/loconet.hpp"
#include "../protocol/loconet/loconet.hpp"
class LocoNetInput : public Input
{
friend class Protocol::LocoNet;
friend class Protocol::LocoNet::LocoNet;
protected:
void worldEvent(WorldState state, WorldEvent event) final;
@ -40,7 +40,7 @@ class LocoNetInput : public Input
CLASS_ID("input.loconet")
CREATE(LocoNetInput)
ObjectProperty<Protocol::LocoNet> loconet;
ObjectProperty<Protocol::LocoNet::LocoNet> loconet;
Property<uint16_t> address;
LocoNetInput(const std::weak_ptr<World> world, std::string_view _id);

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -23,195 +23,26 @@
#include "loconet.hpp"
#include <thread>
#include <chrono>
#include "../../core/eventloop.hpp"
#include "../../core/traintastic.hpp"
#include "../commandstation/commandstation.hpp"
#include "../input/loconetinput.hpp"
#include "../../utils/to_hex.hpp"
#include "../../../core/eventloop.hpp"
#include "../../../core/traintastic.hpp"
#include "../../commandstation/commandstation.hpp"
#include "../../input/loconetinput.hpp"
namespace Protocol {
static std::string to_string(LocoNet::OpCode value)
{
switch(value)
{
case LocoNet::OPC_BUSY: return "OPC_BUSY";
case LocoNet::OPC_GPOFF: return "OPC_GPOFFqqq";
case LocoNet::OPC_GPON: return "OPC_GPON";
case LocoNet::OPC_IDLE: return "OPC_IDLE";
case LocoNet::OPC_LOCO_SPD: return "OPC_LOCO_SPD";
case LocoNet::OPC_LOCO_DIRF: return "OPC_LOCO_DIRF";
case LocoNet::OPC_LOCO_SND: return "OPC_LOCO_SND";
case LocoNet::OPC_SW_REQ: return "OPC_SW_REQ";
case LocoNet::OPC_SW_REP: return "OPC_SW_REP";
case LocoNet::OPC_INPUT_REP: return "OPC_INPUT_REP";
case LocoNet::OPC_LONG_ACK: return "OPC_LONG_ACK";
case LocoNet::OPC_SLOT_STAT1: return "OPC_SLOT_STAT1";
case LocoNet::OPC_CONSIST_FUNC: return "OPC_CONSIST_FUNC";
case LocoNet::OPC_UNLINK_SLOTS: return "OPC_UNLINK_SLOTS";
case LocoNet::OPC_LINK_SLOTS: return "OPC_LINK_SLOTS";
case LocoNet::OPC_MOVE_SLOTS: return "OPC_MOVE_SLOTS";
case LocoNet::OPC_RQ_SL_DATA: return "OPC_RQ_SL_DATA";
case LocoNet::OPC_SW_STATE: return "OPC_SW_STATE";
case LocoNet::OPC_SW_ACK: return "OPC_SW_ACK";
case LocoNet::OPC_LOCO_ADR: return "OPC_LOCO_ADR";
case LocoNet::OPC_MULTI_SENSE: return "OPC_MULTI_SENSE";
case LocoNet::OPC_PEER_XFER: return "OPC_PEER_XFER";
case LocoNet::OPC_SL_RD_DATA: return "OPC_SL_RD_DATA";
case LocoNet::OPC_IMM_PACKET: return "OPC_IMM_PACKET";
case LocoNet::OPC_WR_SL_DATA: return "OPC_WR_SL_DATA";
}
return to_hex(value);
}
std::string to_string(const LocoNet::Message& message, bool raw = false)
{
std::string s{to_string(message.opCode)};
switch(message.opCode)
{
case LocoNet::OPC_GPON:
case LocoNet::OPC_GPOFF:
case LocoNet::OPC_IDLE:
case LocoNet::OPC_BUSY:
break;
case LocoNet::OPC_LOCO_SPD:
{
const LocoNet::LocoSpd& locoSpd = static_cast<const LocoNet::LocoSpd&>(message);
s.append(" slot=").append(std::to_string(locoSpd.slot));
s.append(" speed=").append(std::to_string(locoSpd.speed));
break;
}
case LocoNet::OPC_LOCO_DIRF:
{
const LocoNet::LocoDirF& locoDirF = static_cast<const LocoNet::LocoDirF&>(message);
s.append(" slot=").append(std::to_string(locoDirF.slot));
s.append(" dir=").append(locoDirF.direction() == Direction::Forward ? "fwd" : "rev");
s.append(" f0=").append(locoDirF.f0() ? "on" : "off");
s.append(" f1=").append(locoDirF.f1() ? "on" : "off");
s.append(" f2=").append(locoDirF.f2() ? "on" : "off");
s.append(" f3=").append(locoDirF.f3() ? "on" : "off");
s.append(" f4=").append(locoDirF.f4() ? "on" : "off");
break;
}
case LocoNet::OPC_LOCO_SND:
{
const LocoNet::LocoSnd& locoSnd = static_cast<const LocoNet::LocoSnd&>(message);
s.append(" slot=").append(std::to_string(locoSnd.slot));
s.append(" f5=").append(locoSnd.f5() ? "on" : "off");
s.append(" f6=").append(locoSnd.f6() ? "on" : "off");
s.append(" f7=").append(locoSnd.f7() ? "on" : "off");
s.append(" f8=").append(locoSnd.f8() ? "on" : "off");
break;
}
case LocoNet::OPC_LOCO_F9F12:
{
const LocoNet::LocoF9F12& locoF9F12 = static_cast<const LocoNet::LocoF9F12&>(message);
s.append(" slot=").append(std::to_string(locoF9F12.slot));
s.append(" f9=").append(locoF9F12.f9() ? "on" : "off");
s.append(" f10=").append(locoF9F12.f10() ? "on" : "off");
s.append(" f11=").append(locoF9F12.f11() ? "on" : "off");
s.append(" f12=").append(locoF9F12.f12() ? "on" : "off");
break;
}
case LocoNet::OPC_INPUT_REP:
{
const LocoNet::InputRep& inputRep = static_cast<const LocoNet::InputRep&>(message);
s.append(" address=").append(std::to_string(inputRep.address()));
s.append(" input=").append(inputRep.isAuxInput() ? "aux" : "switch");
s.append(" value=").append(inputRep.value() ? "high" : "low");
break;
}
case LocoNet::OPC_RQ_SL_DATA:
{
const LocoNet::RequestSlotData& requestSlotData = static_cast<const LocoNet::RequestSlotData&>(message);
s.append(" slot=").append(std::to_string(requestSlotData.slot));
break;
}
case LocoNet::OPC_MULTI_SENSE:
{
const LocoNet::MultiSense& multiSense = static_cast<const LocoNet::MultiSense&>(message);
if(multiSense.isTransponder())
{
const LocoNet::MultiSenseTransponder& multiSenseTransponder = static_cast<const LocoNet::MultiSenseTransponder&>(multiSense);
s.append(multiSenseTransponder.isPresent() ? " present" : " absent");
s.append(" sensorAddress=").append(std::to_string(multiSenseTransponder.sensorAddress()));
s.append(" transponderAddress=").append(std::to_string(multiSenseTransponder.transponderAddress()));
}
else
raw = true;
break;
}
case LocoNet::OPC_MULTI_SENSE_LONG:
{
const LocoNet::MultiSenseLong& multiSense = static_cast<const LocoNet::MultiSenseLong&>(message);
if(multiSense.isTransponder())
{
const LocoNet::MultiSenseLongTransponder& multiSenseTransponder = static_cast<const LocoNet::MultiSenseLongTransponder&>(multiSense);
s.append(multiSenseTransponder.isPresent() ? " present" : " absent");
s.append(" sensorAddress=").append(std::to_string(multiSenseTransponder.sensorAddress()));
s.append(" transponderAddress=").append(std::to_string(multiSenseTransponder.transponderAddress()));
s.append(" transponderDirection=").append(multiSenseTransponder.transponderDirection() == Direction::Forward ? "fwd" : "rev");
}
else
raw = true;
break;
}
default:
raw = true;
break;
}
if(raw)
{
s.append(" [");
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&message);
for(int i = 0; i < message.size(); i++)
{
if(i != 0)
s.append(" ");
s.append(to_hex(bytes[i]));
}
s.append("]");
}
return s;
}
namespace Protocol::LocoNet {
void updateDecoderSpeed(const std::shared_ptr<Hardware::Decoder>& decoder, uint8_t speed)
{
decoder->emergencyStop.setValueInternal(speed == LocoNet::SPEED_ESTOP);
decoder->emergencyStop.setValueInternal(speed == SPEED_ESTOP);
if(speed == LocoNet::SPEED_STOP || speed == LocoNet::SPEED_ESTOP)
if(speed == SPEED_STOP || speed == SPEED_ESTOP)
decoder->speedStep.setValueInternal(0);
else
decoder->speedStep.setValueInternal(((speed - 1) * decoder->speedSteps) / (LocoNet::SPEED_MAX - 1));
}
uint8_t LocoNet::calcChecksum(const Message& msg)
{
const uint8_t* p = reinterpret_cast<const uint8_t*>(&msg);
const int size = msg.size() - 1;
uint8_t checksum = 0xFF;
for(int i = 0; i < size; i++)
checksum ^= p[i];
return checksum;
}
void LocoNet::updateChecksum(Message& msg)
{
reinterpret_cast<uint8_t*>(&msg)[msg.size() - 1] = calcChecksum(msg);
}
bool LocoNet::isChecksumValid(const Message& msg)
{
return calcChecksum(msg) == reinterpret_cast<const uint8_t*>(&msg)[msg.size() - 1];
decoder->speedStep.setValueInternal(((speed - 1) * decoder->speedSteps) / (SPEED_MAX - 1));
}
LocoNet::LocoNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send) :
SubObject(_parent, parentPropertyName),
m_commandStation{dynamic_cast<Hardware::CommandStation::CommandStation*>(&_parent)},
m_send{std::move(send)},
m_debugLog{true/*false*/},
m_queryLocoSlots{SLOT_UNKNOWN},
@ -288,10 +119,10 @@ void LocoNet::receive(const Message& message)
EventLoop::call(
[this]()
{
if(auto cs = std::dynamic_pointer_cast<Hardware::CommandStation::CommandStation>(parent().shared_from_this()))
if(m_commandStation)
{
cs->emergencyStop.setValueInternal(false);
cs->trackVoltageOff.setValueInternal(false);
m_commandStation->emergencyStop.setValueInternal(false);
m_commandStation->trackVoltageOff.setValueInternal(false);
}
});
break;
@ -300,8 +131,8 @@ void LocoNet::receive(const Message& message)
EventLoop::call(
[this]()
{
if(auto cs = std::dynamic_pointer_cast<Hardware::CommandStation::CommandStation>(parent().shared_from_this()))
cs->trackVoltageOff.setValueInternal(true);
if(m_commandStation)
m_commandStation->trackVoltageOff.setValueInternal(true);
});
break;
@ -309,8 +140,8 @@ void LocoNet::receive(const Message& message)
EventLoop::call(
[this]()
{
if(auto cs = std::dynamic_pointer_cast<Hardware::CommandStation::CommandStation>(parent().shared_from_this()))
cs->emergencyStop.setValueInternal(true);
if(m_commandStation)
m_commandStation->emergencyStop.setValueInternal(true);
});
break;
@ -461,17 +292,35 @@ void LocoNet::decoderChanged(const Hardware::Decoder& decoder, Hardware::Decoder
decoder.getFunctionValue(12)};
send(decoder.address, message);
}
else if(functionNumber <= 20)
else if(functionNumber <= 19)
{
LocoF13F20 message{
LocoF13F19 message{
decoder.getFunctionValue(13),
decoder.getFunctionValue(14),
decoder.getFunctionValue(15),
decoder.getFunctionValue(16),
decoder.getFunctionValue(17),
decoder.getFunctionValue(18),
decoder.getFunctionValue(19),
decoder.getFunctionValue(20)};
decoder.getFunctionValue(19)};
send(decoder.address, message);
}
else if(functionNumber == 20 || functionNumber == 28)
{
LocoF20F28 message{
decoder.getFunctionValue(20),
decoder.getFunctionValue(28)};
send(decoder.address, message);
}
else if(functionNumber <= 27)
{
LocoF21F27 message{
decoder.getFunctionValue(21),
decoder.getFunctionValue(22),
decoder.getFunctionValue(23),
decoder.getFunctionValue(24),
decoder.getFunctionValue(25),
decoder.getFunctionValue(26),
decoder.getFunctionValue(27)};
send(decoder.address, message);
}
else
@ -490,14 +339,14 @@ std::shared_ptr<Hardware::Decoder> LocoNet::getDecoder(uint8_t slot, bool reques
if(slot < SLOT_LOCO_MIN || slot > SLOT_LOCO_MAX)
return nullptr;
if(auto cs = std::dynamic_pointer_cast<Hardware::CommandStation::CommandStation>(parent().shared_from_this()))
if(m_commandStation)
{
const uint16_t address = m_slots.getAddress(slot);
if(address != 0)
{
auto decoder = cs->getDecoder(DecoderProtocol::DCC, address, isLongAddress(address));
auto decoder = m_commandStation->getDecoder(DecoderProtocol::DCC, address, isLongAddress(address));
if(!decoder)
decoder = cs->getDecoder(DecoderProtocol::Auto, address);
decoder = m_commandStation->getDecoder(DecoderProtocol::Auto, address);
return decoder;
}
else if(request)

Datei anzeigen

@ -0,0 +1,138 @@
/**
* server/src/hardware/protocol/loconet.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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.
*/
/**
* Portions Copyright (C) Digitrax Inc.
*
* LocoNet is a registered trademark of DigiTrax, Inc.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LOCONET_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_LOCONET_HPP
#include <vector>
#include "../../../core/subobject.hpp"
#include "../../../core/property.hpp"
#include <enum/direction.hpp>
#include "../../../enum/loconetcommandstation.hpp"
//#include <cstdint>
//#include <cassert>
#include "../../../hardware/decoder/decoderchangeflags.hpp"
#include "messages.hpp"
namespace Hardware {
namespace CommandStation {
class CommandStation;
}
class Decoder;
}
class LocoNetInput;
namespace Protocol::LocoNet {
class LocoNet : public SubObject
{
//friend class LocoNetInput;
protected:
static constexpr bool isLongAddress(uint16_t address)
{
return address > 127;
}
public:
protected:
class Slots
{
private:
std::unordered_map<uint16_t, uint8_t> m_addressToSlot;
std::unordered_map<uint8_t, uint16_t> m_slotToAddress;
public:
uint8_t getSlot(uint16_t address) const
{
auto it = m_addressToSlot.find(address);
return it != m_addressToSlot.end() ? it->second : SLOT_UNKNOWN;
}
uint16_t getAddress(uint8_t slot) const
{
auto it = m_slotToAddress.find(slot);
return it != m_slotToAddress.end() ? it->second : 0;
}
void set(uint16_t address, uint8_t slot)
{
m_addressToSlot[address] = slot;
m_slotToAddress[slot] = address;
}
void clear()
{
m_addressToSlot.clear();
m_slotToAddress.clear();
}
};
Hardware::CommandStation::CommandStation* m_commandStation; // valid if parent is command station, else nullptr
std::function<bool(const Message&)> m_send;
std::atomic_bool m_debugLog;
Slots m_slots;
std::unordered_map<uint16_t, std::vector<std::byte>> m_slotRequests;
uint8_t m_queryLocoSlots;
std::unordered_map<uint16_t, std::shared_ptr<LocoNetInput>> m_inputs;
std::shared_ptr<Hardware::Decoder> getDecoder(uint8_t slot, bool request = true);
void send(uint16_t address, Message& message, uint8_t& slot);
template<typename T>
inline void send(uint16_t address, T& message)
{
send(address, message, message.slot);
}
public://protected:
bool isInputAddressAvailable(uint16_t address);
bool addInput(const std::shared_ptr<LocoNetInput>& input);
void removeInput(const std::shared_ptr<LocoNetInput>& input);
public:
CLASS_ID("protocol.loconet")
Property<LocoNetCommandStation> commandStation;
Property<bool> debugLog;
LocoNet(Object& _parent, const std::string& parentPropertyName, std::function<bool(const Message&)> send);
bool send(const Message& message);
void receive(const Message& message);
void decoderChanged(const Hardware::Decoder& decoder, Hardware::DecoderChangeFlags changes, uint32_t functionNumber);
void queryLocoSlots();
};
}
#endif

Datei anzeigen

@ -34,6 +34,8 @@ F16 OFF:
2020-07-16 22:51:20.933660 [debug] cs_1.loconet: rx: D4 [D4 20 1F 08 00 1C]
2020-07-16 22:51:20.949613 [debug] cs_1.loconet: rx: D4 [D4 20 1F 05 00 11]
F20 ON:
2020-07-17 11:00:28.212622 [debug] cs_1.loconet: rx: D4 [D4 20 1F 09 01 1C]

Datei anzeigen

@ -0,0 +1,217 @@
/**
* server/src/hardware/protocol/loconet/messages.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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 "messages.hpp"
#include "../../../utils/to_hex.hpp"
namespace Protocol::LocoNet {
uint8_t calcChecksum(const Message& message)
{
const uint8_t* p = reinterpret_cast<const uint8_t*>(&message);
const int size = message.size() - 1;
uint8_t checksum = 0xFF;
for(int i = 0; i < size; i++)
checksum ^= p[i];
return checksum;
}
void updateChecksum(Message& message)
{
reinterpret_cast<uint8_t*>(&message)[message.size() - 1] = calcChecksum(message);
}
bool isChecksumValid(const Message& message)
{
return calcChecksum(message) == reinterpret_cast<const uint8_t*>(&message)[message.size() - 1];
}
std::string to_string(const Message& message, bool raw)
{
std::string s;
if(std::string_view sv = to_string(message.opCode); !sv.empty())
s = sv;
else
s = to_hex(message.opCode);
switch(message.opCode)
{
case OPC_GPON:
case OPC_GPOFF:
case OPC_IDLE:
case OPC_BUSY:
break;
case OPC_LOCO_SPD:
{
const LocoSpd& locoSpd = static_cast<const LocoSpd&>(message);
s.append(" slot=").append(std::to_string(locoSpd.slot));
s.append(" speed=").append(std::to_string(locoSpd.speed));
break;
}
case OPC_LOCO_DIRF:
{
const LocoDirF& locoDirF = static_cast<const LocoDirF&>(message);
s.append(" slot=").append(std::to_string(locoDirF.slot));
s.append(" dir=").append(locoDirF.direction() == Direction::Forward ? "fwd" : "rev");
s.append(" f0=").append(locoDirF.f0() ? "on" : "off");
s.append(" f1=").append(locoDirF.f1() ? "on" : "off");
s.append(" f2=").append(locoDirF.f2() ? "on" : "off");
s.append(" f3=").append(locoDirF.f3() ? "on" : "off");
s.append(" f4=").append(locoDirF.f4() ? "on" : "off");
break;
}
case OPC_LOCO_SND:
{
const LocoSnd& locoSnd = static_cast<const LocoSnd&>(message);
s.append(" slot=").append(std::to_string(locoSnd.slot));
s.append(" f5=").append(locoSnd.f5() ? "on" : "off");
s.append(" f6=").append(locoSnd.f6() ? "on" : "off");
s.append(" f7=").append(locoSnd.f7() ? "on" : "off");
s.append(" f8=").append(locoSnd.f8() ? "on" : "off");
break;
}
case OPC_LOCO_F9F12:
{
const LocoF9F12& locoF9F12 = static_cast<const LocoF9F12&>(message);
s.append(" slot=").append(std::to_string(locoF9F12.slot));
s.append(" f9=").append(locoF9F12.f9() ? "on" : "off");
s.append(" f10=").append(locoF9F12.f10() ? "on" : "off");
s.append(" f11=").append(locoF9F12.f11() ? "on" : "off");
s.append(" f12=").append(locoF9F12.f12() ? "on" : "off");
break;
}
case OPC_INPUT_REP:
{
const InputRep& inputRep = static_cast<const InputRep&>(message);
s.append(" address=").append(std::to_string(inputRep.address()));
s.append(" input=").append(inputRep.isAuxInput() ? "aux" : "switch");
s.append(" value=").append(inputRep.value() ? "high" : "low");
break;
}
case OPC_RQ_SL_DATA:
{
const RequestSlotData& requestSlotData = static_cast<const RequestSlotData&>(message);
s.append(" slot=").append(std::to_string(requestSlotData.slot));
break;
}
case OPC_MULTI_SENSE:
{
const MultiSense& multiSense = static_cast<const MultiSense&>(message);
if(multiSense.isTransponder())
{
const MultiSenseTransponder& multiSenseTransponder = static_cast<const MultiSenseTransponder&>(multiSense);
s.append(multiSenseTransponder.isPresent() ? " present" : " absent");
s.append(" sensorAddress=").append(std::to_string(multiSenseTransponder.sensorAddress()));
s.append(" transponderAddress=").append(std::to_string(multiSenseTransponder.transponderAddress()));
}
else
raw = true;
break;
}
case OPC_D4:
{
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&message);
if(bytes[1] == 0x20)
{
switch(bytes[3])
{
case 0x08:
{
const LocoF13F19& locoF13F19 = static_cast<const LocoF13F19&>(message);
s.append(" slot=").append(std::to_string(locoF13F19.slot));
s.append(" f13=").append(locoF13F19.f13() ? "on" : "off");
s.append(" f14=").append(locoF13F19.f14() ? "on" : "off");
s.append(" f15=").append(locoF13F19.f15() ? "on" : "off");
s.append(" f16=").append(locoF13F19.f16() ? "on" : "off");
s.append(" f17=").append(locoF13F19.f17() ? "on" : "off");
s.append(" f18=").append(locoF13F19.f18() ? "on" : "off");
s.append(" f19=").append(locoF13F19.f19() ? "on" : "off");
break;
}
case 0x05:
{
const LocoF20F28& locoF20F28 = static_cast<const LocoF20F28&>(message);
s.append(" slot=").append(std::to_string(locoF20F28.slot));
s.append(" f20=").append(locoF20F28.f20() ? "on" : "off");
s.append(" f28=").append(locoF20F28.f28() ? "on" : "off");
break;
}
case 0x09:
{
const LocoF21F27& locoF21F27 = static_cast<const LocoF21F27&>(message);
s.append(" slot=").append(std::to_string(locoF21F27.slot));
s.append(" f21=").append(locoF21F27.f21() ? "on" : "off");
s.append(" f22=").append(locoF21F27.f22() ? "on" : "off");
s.append(" f23=").append(locoF21F27.f23() ? "on" : "off");
s.append(" f24=").append(locoF21F27.f24() ? "on" : "off");
s.append(" f25=").append(locoF21F27.f25() ? "on" : "off");
s.append(" f26=").append(locoF21F27.f26() ? "on" : "off");
s.append(" f27=").append(locoF21F27.f27() ? "on" : "off");
break;
}
default:
raw = true;
break;
}
}
else
raw = true;
break;
}
case OPC_MULTI_SENSE_LONG:
{
const MultiSenseLong& multiSense = static_cast<const MultiSenseLong&>(message);
if(multiSense.isTransponder())
{
const MultiSenseLongTransponder& multiSenseTransponder = static_cast<const MultiSenseLongTransponder&>(multiSense);
s.append(multiSenseTransponder.isPresent() ? " present" : " absent");
s.append(" sensorAddress=").append(std::to_string(multiSenseTransponder.sensorAddress()));
s.append(" transponderAddress=").append(std::to_string(multiSenseTransponder.transponderAddress()));
s.append(" transponderDirection=").append(multiSenseTransponder.transponderDirection() == Direction::Forward ? "fwd" : "rev");
}
else
raw = true;
break;
}
default:
raw = true;
break;
}
if(raw)
{
s.append(" [");
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&message);
for(int i = 0; i < message.size(); i++)
{
if(i != 0)
s.append(" ");
s.append(to_hex(bytes[i]));
}
s.append("]");
}
return s;
}
}

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -0,0 +1,112 @@
/**
* server/src/hardware/protocol/loconet/opcode.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2020 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.
*/
/**
* Portions Copyright (C) Digitrax Inc.
*
* LocoNet is a registered trademark of DigiTrax, Inc.
*/
#ifndef TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_OPCODE_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_OPCODE_HPP
#include <cstdint>
#include <string_view>
namespace Protocol::LocoNet {
enum OpCode : uint8_t
{
// 2 byte message opcodes:
OPC_BUSY = 0x81,
OPC_GPOFF = 0x82,
OPC_GPON = 0x83,
OPC_IDLE = 0x85,
// 4 byte message opcodes:
OPC_LOCO_SPD = 0xA0,
OPC_LOCO_DIRF = 0xA1,
OPC_LOCO_SND = 0xA2,
OPC_LOCO_F9F12 = 0xA3, // based on reverse engineering, see loconet.md
OPC_SW_REQ = 0xB0,
OPC_SW_REP = 0xB1,
OPC_INPUT_REP = 0xB2,
OPC_LONG_ACK = 0xB4,
OPC_SLOT_STAT1 = 0xB5,
OPC_CONSIST_FUNC = 0xB6,
OPC_UNLINK_SLOTS = 0xB8,
OPC_LINK_SLOTS = 0xB9,
OPC_MOVE_SLOTS = 0xBA,
OPC_RQ_SL_DATA = 0xBB,
OPC_SW_STATE = 0xBC,
OPC_SW_ACK = 0xBD,
OPC_LOCO_ADR = 0xBF,
// 6 byte message opcodes:
OPC_MULTI_SENSE = 0xD0, // based on reverse engineering, see loconet.md
OPC_D4 = 0xD4,// based on reverse engineering, probably used for multiple sub commands, see loconet.md
// variable byte message opcodes:
OPC_MULTI_SENSE_LONG = 0XE0, // based on reverse engineering, see loconet.md
OPC_PEER_XFER = 0xE5,
OPC_SL_RD_DATA = 0xE7,
OPC_IMM_PACKET = 0xED,
OPC_WR_SL_DATA = 0xEF,
};
constexpr std::string_view to_string(OpCode value)
{
switch(value)
{
case LocoNet::OPC_BUSY: return "OPC_BUSY";
case LocoNet::OPC_GPOFF: return "OPC_GPOFFqqq";
case LocoNet::OPC_GPON: return "OPC_GPON";
case LocoNet::OPC_IDLE: return "OPC_IDLE";
case LocoNet::OPC_LOCO_SPD: return "OPC_LOCO_SPD";
case LocoNet::OPC_LOCO_DIRF: return "OPC_LOCO_DIRF";
case LocoNet::OPC_LOCO_SND: return "OPC_LOCO_SND";
case LocoNet::OPC_SW_REQ: return "OPC_SW_REQ";
case LocoNet::OPC_SW_REP: return "OPC_SW_REP";
case LocoNet::OPC_INPUT_REP: return "OPC_INPUT_REP";
case LocoNet::OPC_LONG_ACK: return "OPC_LONG_ACK";
case LocoNet::OPC_SLOT_STAT1: return "OPC_SLOT_STAT1";
case LocoNet::OPC_CONSIST_FUNC: return "OPC_CONSIST_FUNC";
case LocoNet::OPC_UNLINK_SLOTS: return "OPC_UNLINK_SLOTS";
case LocoNet::OPC_LINK_SLOTS: return "OPC_LINK_SLOTS";
case LocoNet::OPC_MOVE_SLOTS: return "OPC_MOVE_SLOTS";
case LocoNet::OPC_RQ_SL_DATA: return "OPC_RQ_SL_DATA";
case LocoNet::OPC_SW_STATE: return "OPC_SW_STATE";
case LocoNet::OPC_SW_ACK: return "OPC_SW_ACK";
case LocoNet::OPC_LOCO_ADR: return "OPC_LOCO_ADR";
case LocoNet::OPC_MULTI_SENSE: return "OPC_MULTI_SENSE";
case LocoNet::OPC_PEER_XFER: return "OPC_PEER_XFER";
case LocoNet::OPC_SL_RD_DATA: return "OPC_SL_RD_DATA";
case LocoNet::OPC_IMM_PACKET: return "OPC_IMM_PACKET";
case LocoNet::OPC_WR_SL_DATA: return "OPC_WR_SL_DATA";
}
return {};
}
}
#endif