loconet: updated OPC_MULTI_SENSE_LONG

based on documentation from the developer of the message format.
Dieser Commit ist enthalten in:
Reinder Feenstra 2023-10-21 13:34:19 +02:00
Ursprung a457088f29
Commit 8ff6a98dc8
5 geänderte Dateien mit 283 neuen und 71 gelöschten Zeilen

Datei anzeigen

@ -764,17 +764,17 @@ void Kernel::receive(const Message& message)
case OPC_MULTI_SENSE_LONG:
{
const MultiSenseLong& multiSense = static_cast<const MultiSenseLong&>(message);
if(multiSense.isTransponder())
if(multiSense.code() == MultiSenseLong::Code::ReleaseTransponder || multiSense.code() == MultiSenseLong::Code::DetectTransponder)
{
EventLoop::call(
[this, multiSenseTransponder=static_cast<const MultiSenseLongTransponder&>(multiSense)]()
[this, multiSense]()
{
m_identificationController->identificationEvent(
IdentificationController::defaultIdentificationChannel,
1 + multiSenseTransponder.sensorAddress(),
multiSenseTransponder.isPresent() ? IdentificationEventType::Present : IdentificationEventType::Absent,
multiSenseTransponder.transponderAddress(),
multiSenseTransponder.transponderDirection(),
1 + multiSense.sensorAddress(),
multiSense.code() == MultiSenseLong::Code::DetectTransponder ? IdentificationEventType::Present : IdentificationEventType::Absent,
multiSense.transponderAddress(),
multiSense.transponderDirection(),
0);
});
}

Datei anzeigen

@ -0,0 +1,136 @@
/**
* server/src/hardware/protocol/loconet/message/multisenselong.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023 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_LOCONET_MESSAGE_MULTISENSELONG_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_LOCONET_MESSAGE_MULTISENSELONG_HPP
#include "message.hpp"
#include <traintastic/enum/direction.hpp>
#include "../../railcom/appdynid.hpp"
namespace LocoNet {
struct MultiSenseLong : Message
{
enum class Code
{
ReleaseTransponder = 0x00,
DetectTransponder = 0x20,
RailComAppDyn = 0x40,
Reserved = 0x60,
};
uint8_t len;
uint8_t data1;
uint8_t data2;
uint8_t data3;
uint8_t data4;
uint8_t data5;
uint8_t data6;
uint8_t checksum;
MultiSenseLong() :
Message(OPC_MULTI_SENSE_LONG),
len{9}
{
}
Code code() const
{
return static_cast<Code>(data1 & 0x60);
}
uint16_t sensorAddress() const
{
return (static_cast<uint16_t>(data1 & 0x1F) << 7) | (data2 & 0x7F);
}
uint16_t transponderAddress() const
{
if(isTransponderAddressLong())
return (static_cast<uint16_t>(data3 & 0x7F) << 7) | (data4 & 0x7F);
else
return (data4 & 0x7F);
}
bool isTransponderAddressLong() const
{
return data3 != 0xFD;
}
Direction transponderDirection() const
{
return (data5 & 0x40) ? Direction::Forward : Direction::Reverse;
}
};
static_assert(sizeof(MultiSenseLong) == 9);
struct MultiSenseLongRailComAppDyn : MultiSenseLong
{
RailCom::AppDynId appDynId() const
{
return static_cast<RailCom::AppDynId>((data5 >> 1) & 0x1F);
}
uint8_t value() const
{
return ((data5 & 0x01) << 7) | data6;
}
};
static_assert(sizeof(MultiSenseLongRailComAppDyn) == 9);
struct MultiSenseLongRailComAppDynActualSpeed : MultiSenseLongRailComAppDyn
{
uint16_t actualSpeed() const
{
if(appDynId() == RailCom::AppDynId::ActualSpeed)
{
return value();
}
assert(appDynId() == RailCom::AppDynId::ActualSpeedHigh);
return 0x100 + value();
}
};
static_assert(sizeof(MultiSenseLongRailComAppDynActualSpeed) == 9);
}
constexpr std::string_view toString(LocoNet::MultiSenseLong::Code value)
{
switch(value)
{
case LocoNet::MultiSenseLong::Code::ReleaseTransponder:
return "Release";
case LocoNet::MultiSenseLong::Code::DetectTransponder:
return "Detect";
case LocoNet::MultiSenseLong::Code::RailComAppDyn:
return "RailComAppDyn";
case LocoNet::MultiSenseLong::Code::Reserved:
return "Reserved";
}
return {};
}
#endif

Datei anzeigen

@ -3,7 +3,7 @@
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2022 Reinder Feenstra
* Copyright (C) 2019-2023 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -357,13 +357,26 @@ std::string toString(const Message& message)
case OPC_MULTI_SENSE_LONG:
{
const MultiSenseLong& multiSense = static_cast<const MultiSenseLong&>(message);
if(multiSense.isTransponder())
s.append(::toString(multiSense.code()));
s.append(" sensorAddress=").append(std::to_string(multiSense.sensorAddress()));
s.append(" transponderAddress=").append(std::to_string(multiSense.transponderAddress()));
s.append(" transponderDirection=").append(multiSense.transponderDirection() == Direction::Forward ? "fwd" : "rev");
if(multiSense.code() == MultiSenseLong::Code::RailComAppDyn)
{
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");
const auto& multiSenseRailComAppDyn = static_cast<const MultiSenseLongRailComAppDyn&>(multiSense);
s.append(" app_dyn=").append(std::to_string(static_cast<uint8_t>(multiSenseRailComAppDyn.appDynId())));
if(auto sv = ::toString(multiSenseRailComAppDyn.appDynId()); !sv.empty())
{
s.append(" ()").append(sv).append(")");
}
if(RailCom::isAppDynActualSpeed(multiSenseRailComAppDyn.appDynId()))
{
s.append(" actual_speed=").append(std::to_string(static_cast<const MultiSenseLongRailComAppDynActualSpeed&>(multiSenseRailComAppDyn).actualSpeed()));
}
else
{
s.append(" value=").append(std::to_string(multiSenseRailComAppDyn.value()));
}
}
break;
}

Datei anzeigen

@ -41,6 +41,7 @@
#include "message/locof9f12imm.hpp"
#include "message/locof13f20imm.hpp"
#include "message/locof21f28imm.hpp"
#include "message/multisenselong.hpp"
#include "message/uhlenbrock.hpp"
namespace LocoNet {
@ -110,6 +111,7 @@ constexpr uint8_t SW2_DIR = 0x20;
constexpr uint8_t MULTI_SENSE_TYPE_MASK = 0xE0;
constexpr uint8_t MULTI_SENSE_TYPE_TRANSPONDER_GONE = 0x00;
constexpr uint8_t MULTI_SENSE_TYPE_TRANSPONDER_PRESENT = 0x20;
constexpr uint8_t MULTI_SENSE_LONG_TYPE_RAILCOM_APP_DYN = 0x40;
constexpr uint8_t MULTI_SENSE_TRANSPONDER_ADDRESS_SHORT = 0xFD;
struct SlotMessage : Message
@ -1059,64 +1061,6 @@ struct MultiSenseTransponder : MultiSense
};
static_assert(sizeof(MultiSenseTransponder) == 6);
struct MultiSenseLong : Message
{
uint8_t len;
uint8_t data1;
uint8_t data2;
uint8_t data3;
uint8_t data4;
uint8_t data5;
uint8_t data6;
uint8_t checksum;
MultiSenseLong() :
Message(OPC_MULTI_SENSE_LONG),
len{9}
{
}
bool isTransponder() const
{
return
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_GONE) ||
((data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT);
}
};
static_assert(sizeof(MultiSenseLong) == 9);
struct MultiSenseLongTransponder : MultiSenseLong
{
bool isPresent() const
{
return (data1 & MULTI_SENSE_TYPE_MASK) == MULTI_SENSE_TYPE_TRANSPONDER_PRESENT;
}
uint16_t sensorAddress() const
{
return (static_cast<uint16_t>(data1 & 0x1F) << 7) | (data2 & 0x7F);
}
uint16_t transponderAddress() const
{
if(isTransponderAddressLong())
return (static_cast<uint16_t>(data3 & 0x7F) << 7) | (data4 & 0x7F);
else
return (data4 & 0x7F);
}
bool isTransponderAddressLong() const
{
return data3 != MULTI_SENSE_TRANSPONDER_ADDRESS_SHORT;
}
Direction transponderDirection() const
{
return (data5 & 0x40) ? Direction::Forward : Direction::Reverse;
}
};
static_assert(sizeof(MultiSenseLongTransponder) == 9);
struct SlotReadDataBase : Message
{
uint8_t len;

Datei anzeigen

@ -0,0 +1,119 @@
/**
* server/src/hardware/protocol/railcom/appdynid.hpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2023 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_RAILCOM_APPDYNID_HPP
#define TRAINTASTIC_SERVER_HARDWARE_PROTOCOL_RAILCOM_APPDYNID_HPP
#include <cstdint>
#include "../../../utils/inrange.hpp"
namespace RailCom {
enum class AppDynId : uint8_t
{
ActualSpeed = 0,
ActualSpeedHigh = 1,
QualityOfService = 7,
Container1 = 8,
Container2 = 9,
Container3 = 10,
Container4 = 11,
Container5 = 12,
Container6 = 13,
Container7 = 14,
Container8 = 15,
Container9 = 16,
Container10 = 17,
Container11 = 18,
Container12 = 19,
};
constexpr bool isAppDynActualSpeed(AppDynId value)
{
return
(value == RailCom::AppDynId::ActualSpeed) ||
(value == RailCom::AppDynId::ActualSpeedHigh);
}
constexpr bool isAppDynContainer(AppDynId value)
{
return inRange(
static_cast<uint8_t>(value),
static_cast<uint8_t>(RailCom::AppDynId::Container1),
static_cast<uint8_t>(RailCom::AppDynId::Container12));
}
}
constexpr std::string_view toString(RailCom::AppDynId value)
{
switch(value)
{
case RailCom::AppDynId::ActualSpeed:
return "ActualSpeed";
case RailCom::AppDynId::ActualSpeedHigh:
return "ActualSpeedHigh";
case RailCom::AppDynId::QualityOfService:
return "QualityOfService";
case RailCom::AppDynId::Container1:
return "Container1";
case RailCom::AppDynId::Container2:
return "Container2";
case RailCom::AppDynId::Container3:
return "Container3";
case RailCom::AppDynId::Container4:
return "Container4";
case RailCom::AppDynId::Container5:
return "Container5";
case RailCom::AppDynId::Container6:
return "Container6";
case RailCom::AppDynId::Container7:
return "Container7";
case RailCom::AppDynId::Container8:
return "Container8";
case RailCom::AppDynId::Container9:
return "Container9";
case RailCom::AppDynId::Container10:
return "Container10";
case RailCom::AppDynId::Container11:
return "Container11";
case RailCom::AppDynId::Container12:
return "Container12";
}
return {};
}
#endif