diff --git a/server/src/hardware/protocol/loconet/kernel.cpp b/server/src/hardware/protocol/loconet/kernel.cpp index f5f72961..1d021d1d 100644 --- a/server/src/hardware/protocol/loconet/kernel.cpp +++ b/server/src/hardware/protocol/loconet/kernel.cpp @@ -764,17 +764,17 @@ void Kernel::receive(const Message& message) case OPC_MULTI_SENSE_LONG: { const MultiSenseLong& multiSense = static_cast(message); - if(multiSense.isTransponder()) + if(multiSense.code() == MultiSenseLong::Code::ReleaseTransponder || multiSense.code() == MultiSenseLong::Code::DetectTransponder) { EventLoop::call( - [this, multiSenseTransponder=static_cast(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); }); } diff --git a/server/src/hardware/protocol/loconet/message/multisenselong.hpp b/server/src/hardware/protocol/loconet/message/multisenselong.hpp new file mode 100644 index 00000000..791d50bf --- /dev/null +++ b/server/src/hardware/protocol/loconet/message/multisenselong.hpp @@ -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 +#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(data1 & 0x60); + } + + uint16_t sensorAddress() const + { + return (static_cast(data1 & 0x1F) << 7) | (data2 & 0x7F); + } + + uint16_t transponderAddress() const + { + if(isTransponderAddressLong()) + return (static_cast(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((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 diff --git a/server/src/hardware/protocol/loconet/messages.cpp b/server/src/hardware/protocol/loconet/messages.cpp index 9a148757..cc6b7995 100644 --- a/server/src/hardware/protocol/loconet/messages.cpp +++ b/server/src/hardware/protocol/loconet/messages.cpp @@ -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(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(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(multiSense); + s.append(" app_dyn=").append(std::to_string(static_cast(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(multiSenseRailComAppDyn).actualSpeed())); + } + else + { + s.append(" value=").append(std::to_string(multiSenseRailComAppDyn.value())); + } } break; } diff --git a/server/src/hardware/protocol/loconet/messages.hpp b/server/src/hardware/protocol/loconet/messages.hpp index 0a98ba4b..d3940cbd 100644 --- a/server/src/hardware/protocol/loconet/messages.hpp +++ b/server/src/hardware/protocol/loconet/messages.hpp @@ -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(data1 & 0x1F) << 7) | (data2 & 0x7F); - } - - uint16_t transponderAddress() const - { - if(isTransponderAddressLong()) - return (static_cast(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; diff --git a/server/src/hardware/protocol/railcom/appdynid.hpp b/server/src/hardware/protocol/railcom/appdynid.hpp new file mode 100644 index 00000000..9406b7f3 --- /dev/null +++ b/server/src/hardware/protocol/railcom/appdynid.hpp @@ -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 +#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(value), + static_cast(RailCom::AppDynId::Container1), + static_cast(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