-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds implementation for railcom messages related to logon (#562)
- Adds constants for the automatic logon railcom messages - Adds helper functions in the RailcomDefs to generate the S-9.2.1.1 / RCN-218 feedback messages. - Adds a processor for a railcom hub that recognizes these feedback messages and checks for correctness. === * Adds code for parsing 254 railcom feedback messages. * Adds skeleton for state flow handling the logon sequence. * Adds declarations for track interface. * Adds implementation of the most important feedback messages. * Adds support for decoder ID replies to logon enable. * Adds implementation for shortinfo reply. * Adds implementation for Logon Assign feedback. * Fix whitespace * Fixes comments.
- Loading branch information
1 parent
87be9a8
commit bd2f9ec
Showing
8 changed files
with
814 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/** \copyright | ||
* Copyright (c) 2021, Balazs Racz | ||
* All rights reserved. | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions are met: | ||
* | ||
* - Redistributions of source code must retain the above copyright notice, | ||
* this list of conditions and the following disclaimer. | ||
* | ||
* - Redistributions in binary form must reproduce the above copyright notice, | ||
* this list of conditions and the following disclaimer in the documentation | ||
* and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
* POSSIBILITY OF SUCH DAMAGE. | ||
* | ||
* \file Logon.hxx | ||
* Control flows for supporting DCC Automatic Logon. | ||
* | ||
* @author Balazs Racz | ||
* @date 12 Aug 2021 | ||
*/ | ||
|
||
#ifndef _DCC_LOGON_HXX_ | ||
#define _DCC_LOGON_HXX_ | ||
|
||
#include "dcc/PacketSource.hxx" | ||
#include "dcc/TrackIf.hxx" | ||
#include "dcc/UpdateLoop.hxx" | ||
#include "dcc/LogonFeedback.hxx" | ||
#include "executor/StateFlow.hxx" | ||
|
||
namespace dcc | ||
{ | ||
|
||
/// This class needs to be a base class for the template argument of the Logon | ||
/// Handler. | ||
class LogonHandlerModule | ||
{ | ||
|
||
}; // LogonHandlerModule | ||
|
||
/// Handles the automatic logon flow for DCC decoders. | ||
template <class Module> | ||
class LogonHandler : public NonTrainPacketSource, | ||
public StateFlowBase, | ||
public RailcomHubPortInterface | ||
{ | ||
public: | ||
/// Constructor | ||
/// | ||
/// @param service points to the executor to use. | ||
/// @param track pointer to the track interface to send DCC packets to. | ||
/// @param rcom_hub will register to this railcom hub to get feedback. | ||
LogonHandler(Service *service, TrackIf *track, RailcomHubFlow *rcom_hub) | ||
: StateFlowBase(service) | ||
, trackIf_(track) | ||
{ | ||
} | ||
|
||
/// Initiates a logon sequence at startup. | ||
void startup_logon() | ||
{ | ||
start_flow(STATE(startup_logon)); | ||
} | ||
|
||
private: | ||
Action startup_logon() | ||
{ | ||
} | ||
|
||
/// If we need to send packets to the track, we can do it here directly. | ||
TrackIf *trackIf_; | ||
}; // LogonHandler | ||
|
||
} // namespace dcc | ||
|
||
#endif // _DCC_LOGON_HXX_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
#include "dcc/LogonFeedback.hxx" | ||
|
||
#include "utils/test_main.hxx" | ||
|
||
using ::testing::Return; | ||
|
||
namespace dcc | ||
{ | ||
|
||
class MockFeedbackCallbacks : public LogonFeedbackCallbacks | ||
{ | ||
public: | ||
MOCK_METHOD1( | ||
classify_packet, LogonFeedbackCallbacks::PacketType(uintptr_t)); | ||
MOCK_METHOD3(process_select_shortinfo, | ||
void(uintptr_t feedback_key, bool error, uint64_t data)); | ||
|
||
MOCK_METHOD3(process_logon_assign, | ||
void(uintptr_t feedback_key, bool error, uint64_t data)); | ||
|
||
MOCK_METHOD3(process_decoder_id, | ||
void(uintptr_t feedback_key, bool error, uint64_t data)); | ||
}; | ||
|
||
class ParseFeedbackTest : public ::testing::Test | ||
{ | ||
protected: | ||
/// Creates a valid 8-byte railcom feedback in fb_. | ||
void create_valid_code() | ||
{ | ||
RailcomDefs::append12(15, 0x44, fb_.ch1Data); | ||
fb_.ch1Size = 2; | ||
|
||
RailcomDefs::append36(0xa, 0x11223344, fb_.ch2Data); | ||
fb_.ch2Size = 6; | ||
} | ||
|
||
/// Sends the current value in fb_ to the railcom hub. | ||
void send() | ||
{ | ||
auto *b = hub_.alloc(); | ||
b->data()->value() = fb_; | ||
hub_.send(b); | ||
wait_for_main_executor(); | ||
} | ||
|
||
::testing::StrictMock<MockFeedbackCallbacks> cb_; | ||
dcc::Feedback fb_; | ||
RailcomHubFlow hub_ {&g_service}; | ||
LogonFeedbackParser parser_ {&cb_, &hub_}; | ||
}; | ||
|
||
TEST_F(ParseFeedbackTest, valid_code) | ||
{ | ||
create_valid_code(); | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(0u, d >> LogonFeedbackParser::ERROR_SHIFT); | ||
EXPECT_EQ(8u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
EXPECT_EQ(0xf44a11223344u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, conflict_code) | ||
{ | ||
create_valid_code(); | ||
fb_.ch1Data[1] |= 0xFF; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(LogonFeedbackParser::ERROR_GARBAGE | | ||
LogonFeedbackParser::ERROR_OUT_OF_ORDER, | ||
d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(7u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
// Some bits are blanked out. | ||
EXPECT_EQ(0xf40a11223344u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, short_code) | ||
{ | ||
create_valid_code(); | ||
fb_.ch2Size = 4; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(LogonFeedbackParser::ERROR_MISSING_DATA, | ||
d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(6u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
// Some bits are blanked out. | ||
EXPECT_EQ(0xf44a11223000u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, short_code_ack) | ||
{ | ||
create_valid_code(); | ||
RailcomDefs::append36(0xa, 0x11220000, fb_.ch2Data); | ||
fb_.ch2Data[5] = RailcomDefs::CODE_ACK; | ||
fb_.ch2Data[4] = RailcomDefs::CODE_ACK2; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ( | ||
LogonFeedbackParser::ERROR_ACK, d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(6u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
// Some bits are blanked out. | ||
EXPECT_EQ(0xf44a11220000u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, out_of_order_ack) | ||
{ | ||
create_valid_code(); | ||
fb_.ch1Data[1] = RailcomDefs::CODE_ACK; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(LogonFeedbackParser::ERROR_ACK | | ||
LogonFeedbackParser::ERROR_OUT_OF_ORDER, | ||
d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(7u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
// Some bits are blanked out. | ||
EXPECT_EQ(0xf40a11223344u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, one_ack) | ||
{ | ||
fb_.ch1Data[0] = RailcomDefs::CODE_ACK; | ||
fb_.ch1Size = 1; | ||
fb_.ch2Size = 0; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(LogonFeedbackParser::ERROR_ACK | | ||
LogonFeedbackParser::ERROR_MISSING_DATA, | ||
d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(0u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
EXPECT_EQ(0u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, unknown) | ||
{ | ||
create_valid_code(); | ||
fb_.ch1Data[1] = RailcomDefs::CODE_BUSY; | ||
uint64_t d = LogonFeedbackParser::parse_code(&fb_); | ||
EXPECT_EQ(LogonFeedbackParser::ERROR_UNKNOWN | | ||
LogonFeedbackParser::ERROR_OUT_OF_ORDER, | ||
d & LogonFeedbackParser::ERROR_MASK); | ||
EXPECT_EQ(7u, ((d >> LogonFeedbackParser::LENGTH_SHIFT) & 0xff)); | ||
// Some bits are blanked out. | ||
EXPECT_EQ(0xf40a11223344u, (d & LogonFeedbackParser::PAYLOAD_MASK)); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, decoder_id_reply) | ||
{ | ||
constexpr uint64_t decoder_id = 0x79944332211ull; | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_did_feedback(decoder_id, &fb_); | ||
fb_.feedbackKey = key; | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::LOGON_ENABLE)); | ||
EXPECT_CALL( | ||
cb_, process_decoder_id(key, false, decoder_id | (15ull << 44))); | ||
send(); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, short_info_reply) | ||
{ | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_shortinfo_feedback(0x1382, 0x55, 0xa7, 0x03, &fb_); | ||
fb_.feedbackKey = key; | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::SELECT_SHORTINFO)); | ||
EXPECT_CALL(cb_, process_select_shortinfo(key, false, 0x938255a7030bull)); | ||
send(); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, short_info_crc_error) | ||
{ | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_shortinfo_feedback(0x1382, 0x55, 0xa7, 0x03, &fb_); | ||
fb_.feedbackKey = key; | ||
fb_.ch2Data[5] = railcom_encode[0x3f]; | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::SELECT_SHORTINFO)); | ||
// Reports error | ||
EXPECT_CALL(cb_, process_select_shortinfo(key, true, 0x938255a7033full)); | ||
send(); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, short_info_garbage) | ||
{ | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_shortinfo_feedback(0x1382, 0x55, 0xa7, 0x03, &fb_); | ||
fb_.feedbackKey = key; | ||
fb_.ch2Data[5] = 0xF3; // invalid 4/8 code | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::SELECT_SHORTINFO)); | ||
// Reports error | ||
EXPECT_CALL(cb_, process_select_shortinfo(key, true, 0x938255a70300ull)); | ||
send(); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, assign_response) | ||
{ | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_assign_feedback(0x5a, 0x327, 0x18, 0x22, &fb_); | ||
fb_.feedbackKey = key; | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::LOGON_ASSIGN)); | ||
EXPECT_CALL(cb_, process_logon_assign(key, false, 0xd5a327182246ull)); | ||
send(); | ||
} | ||
|
||
TEST_F(ParseFeedbackTest, assign_response_crc_error) | ||
{ | ||
constexpr uintptr_t key = 0x5a5a5a5a; | ||
RailcomDefs::add_assign_feedback(0x5a, 0x327, 0x18, 0x22, &fb_); | ||
fb_.feedbackKey = key; | ||
fb_.ch2Data[5] = railcom_encode[0x3f]; | ||
::testing::InSequence sq; | ||
EXPECT_CALL(cb_, classify_packet(key)) | ||
.WillOnce(Return(LogonFeedbackCallbacks::LOGON_ASSIGN)); | ||
// Reports error | ||
EXPECT_CALL(cb_, process_logon_assign(key, true, 0xd5a32718227full)); | ||
send(); | ||
} | ||
|
||
} // namespace dcc |
Oops, something went wrong.