diff --git a/src/openlcb/TractionConsist.cxxtest b/src/openlcb/TractionConsist.cxxtest index 92c764435..434bdb115 100644 --- a/src/openlcb/TractionConsist.cxxtest +++ b/src/openlcb/TractionConsist.cxxtest @@ -1,60 +1,83 @@ #include "utils/async_traction_test_helper.hxx" -#include "openlcb/TractionTrain.hxx" #include "openlcb/TractionTestTrain.hxx" #include "openlcb/TractionThrottle.hxx" +#include "openlcb/TractionTrain.hxx" namespace openlcb { -static constexpr NodeID nodeIdLead = 0x060100000000 | 1371; -static constexpr NodeID nodeIdC1 = 0x060100000000 | 1372; -static constexpr NodeID nodeIdC2 = 0x060100000000 | 1373; +static constexpr NodeID nodeIdLead = 0x060100000000 | 1370; +static constexpr NodeID nodeIdC1 = 0x060100000000 | 1371; +static constexpr NodeID nodeIdC2 = 0x060100000000 | 1372; +static constexpr NodeID nodeIdC3 = 0x060100000000 | 1373; +static constexpr NodeID nodeIdC4 = 0x060100000000 | 1374; +static constexpr NodeID nodeIdC5 = 0x060100000000 | 1375; -class ConsistTest : public TractionTest { +class ConsistTest : public TractionTest +{ protected: - ConsistTest() { + ConsistTest() + { create_allocated_alias(); run_x([this]() { - otherIf_.local_aliases()->add(nodeIdLead, 0x771); - otherIf_.local_aliases()->add(nodeIdC1, 0x772); - otherIf_.local_aliases()->add(nodeIdC2, 0x773); + otherIf_.local_aliases()->add(nodeIdLead, 0x770); + otherIf_.local_aliases()->add(nodeIdC1, 0x771); + otherIf_.local_aliases()->add(nodeIdC2, 0x772); + otherIf_.local_aliases()->add(nodeIdC3, 0x773); + otherIf_.remote_aliases()->add(nodeIdC4, 0x774); + otherIf_.remote_aliases()->add(nodeIdC5, 0x775); }); nodeLead_.reset(new TrainNodeForProxy(&trainService_, &trainLead_)); nodeC1_.reset(new TrainNodeForProxy(&trainService_, &trainC1_)); nodeC2_.reset(new TrainNodeForProxy(&trainService_, &trainC2_)); + nodeC3_.reset(new TrainNodeForProxy(&trainService_, &trainC3_)); + wait(); + auto b = invoke_flow(&throttle_, TractionThrottleCommands::ASSIGN_TRAIN, + nodeIdLead, false); + EXPECT_EQ(0, b->data()->resultCode); wait(); } - TractionThrottle throttle_{node_}; + void create_consist() + { + auto b = invoke_flow( + &throttle_, TractionThrottleCommands::CONSIST_ADD, nodeIdC1, 0); + ASSERT_EQ(0, b->data()->resultCode); + b = invoke_flow(&throttle_, TractionThrottleCommands::CONSIST_ADD, + nodeIdC2, + TractionDefs::CNSTFLAGS_REVERSE | TractionDefs::CNSTFLAGS_LINKF0); + ASSERT_EQ(0, b->data()->resultCode); + b = invoke_flow(&throttle_, TractionThrottleCommands::CONSIST_ADD, + nodeIdC3, + TractionDefs::CNSTFLAGS_REVERSE | TractionDefs::CNSTFLAGS_LINKF0 | + TractionDefs::CNSTFLAGS_LINKFN); + ASSERT_EQ(0, b->data()->resultCode); + wait(); + } - IfCan otherIf_{&g_executor, &can_hub0, 5, 5, 5}; - TrainService trainService_{&otherIf_}; + TractionThrottle throttle_ {node_}; - LoggingTrain trainLead_{1371}; - LoggingTrain trainC1_{1372}; - LoggingTrain trainC2_{1373}; + IfCan otherIf_ {&g_executor, &can_hub0, 5, 5, 5}; + TrainService trainService_ {&otherIf_}; + + LoggingTrain trainLead_ {1370}; + LoggingTrain trainC1_ {1371}; + LoggingTrain trainC2_ {1372}; + LoggingTrain trainC3_ {1373}; std::unique_ptr nodeLead_; std::unique_ptr nodeC1_; std::unique_ptr nodeC2_; + std::unique_ptr nodeC3_; }; -TEST_F(ConsistTest, CreateDestroy) { +TEST_F(ConsistTest, CreateDestroy) +{ } -TEST_F(ConsistTest, CreateAndRunConsist) { - auto b = invoke_flow( - &throttle_, TractionThrottleCommands::ASSIGN_TRAIN, nodeIdLead, false); - ASSERT_EQ(0, b->data()->resultCode); - wait(); - - b = invoke_flow( - &throttle_, TractionThrottleCommands::CONSIST_ADD, nodeIdC1, 0); - ASSERT_EQ(0, b->data()->resultCode); - b = invoke_flow(&throttle_, TractionThrottleCommands::CONSIST_ADD, nodeIdC2, - TractionDefs::CNSTFLAGS_REVERSE | TractionDefs::CNSTFLAGS_LINKF0); - ASSERT_EQ(0, b->data()->resultCode); - +TEST_F(ConsistTest, CreateAndRunConsist) +{ + create_consist(); Velocity v; v.set_mph(37.5); throttle_.set_speed(v); @@ -78,9 +101,67 @@ TEST_F(ConsistTest, CreateAndRunConsist) { EXPECT_EQ(Velocity::REVERSE, trainLead_.get_speed().direction()); EXPECT_EQ(Velocity::REVERSE, trainC1_.get_speed().direction()); EXPECT_EQ(Velocity::FORWARD, trainC2_.get_speed().direction()); + + EXPECT_FALSE(trainLead_.get_fn(0)); + EXPECT_FALSE(trainLead_.get_fn(2)); + EXPECT_FALSE(trainC1_.get_fn(0)); + EXPECT_FALSE(trainC1_.get_fn(2)); + EXPECT_FALSE(trainC2_.get_fn(0)); + EXPECT_FALSE(trainC2_.get_fn(2)); + EXPECT_FALSE(trainC3_.get_fn(0)); + EXPECT_FALSE(trainC3_.get_fn(2)); + + throttle_.set_fn(0, 1); + wait(); + + // F0 forwarded to C2 and C3, not to C1. + EXPECT_TRUE(trainLead_.get_fn(0)); + EXPECT_FALSE(trainC1_.get_fn(0)); + EXPECT_TRUE(trainC2_.get_fn(0)); + EXPECT_TRUE(trainC3_.get_fn(0)); + + throttle_.set_fn(2, 1); + wait(); + + // F2 forwarded to C2 and C3, not to C0. + EXPECT_TRUE(trainLead_.get_fn(0)); + EXPECT_FALSE(trainC1_.get_fn(0)); + EXPECT_TRUE(trainC2_.get_fn(0)); + EXPECT_TRUE(trainC3_.get_fn(0)); } +TEST_F(ConsistTest, ListenerExpectations) +{ + auto b = + invoke_flow(&throttle_, TractionThrottleCommands::CONSIST_ADD, nodeIdC4, + TractionDefs::CNSTFLAGS_REVERSE | TractionDefs::CNSTFLAGS_LINKF0 | + TractionDefs::CNSTFLAGS_LINKFN); + ASSERT_EQ(0, b->data()->resultCode); + b = invoke_flow(&throttle_, TractionThrottleCommands::CONSIST_ADD, nodeIdC5, + TractionDefs::CNSTFLAGS_LINKF0 | TractionDefs::CNSTFLAGS_LINKFN); + ASSERT_EQ(0, b->data()->resultCode); + + clear_expect(true); + Velocity v; + v.set_mph(37.5); + // Throttle to lead + expect_packet(":X195EB22AN0770004C31;"); + // Lead to follower with listening flag and reversed value + expect_packet(":X195EB770N077480CC31;"); + // Lead to follower with listening flag and regular value + expect_packet(":X195EB770N0775804C31;"); + throttle_.set_speed(v); + wait(); + // Throttle to lead + expect_packet(":X195EB22AN0770010000020001;"); + // Lead to follower with listening flag + expect_packet(":X195EB770N0774810000020001;"); + // Lead to follower with listening flag + expect_packet(":X195EB770N0775810000020001;"); + throttle_.set_fn(2, 1); + wait(); +} } // namespace openlcb diff --git a/src/openlcb/TractionDefs.hxx b/src/openlcb/TractionDefs.hxx index 0d05b9f5a..389a984f4 100644 --- a/src/openlcb/TractionDefs.hxx +++ b/src/openlcb/TractionDefs.hxx @@ -107,6 +107,12 @@ struct TractionDefs { REQ_CONSIST_CONFIG = 0x30, REQ_TRACTION_MGMT = 0x40, + /// Mask to apply to the command byte of the requests. + REQ_MASK = 0x7F, + /// Flag bit in the command byte set when a listener command is + /// forwarded. + REQ_LISTENER = 0x80, + // Byte 1 of REQ_CONTROLLER_CONFIG command CTRLREQ_ASSIGN_CONTROLLER = 0x01, CTRLREQ_RELEASE_CONTROLLER = 0x02, diff --git a/src/openlcb/TractionThrottle.hxx b/src/openlcb/TractionThrottle.hxx index cb7a400be..3863f45e0 100644 --- a/src/openlcb/TractionThrottle.hxx +++ b/src/openlcb/TractionThrottle.hxx @@ -739,7 +739,7 @@ private: const Payload &p = msg->data()->payload; if (p.size() < 1) return; - switch (p[0]) + switch (p[0] & TractionDefs::REQ_MASK) { case TractionDefs::REQ_SET_SPEED: { diff --git a/src/openlcb/TractionTrain.cxx b/src/openlcb/TractionTrain.cxx index 908b046de..5038c0963 100644 --- a/src/openlcb/TractionTrain.cxx +++ b/src/openlcb/TractionTrain.cxx @@ -190,7 +190,7 @@ struct TrainService::Impl LOG(VERBOSE, "Traction message with no command byte."); return reject_permanent(); } - uint8_t cmd = payload()[0]; + uint8_t cmd = payload()[0] & TractionDefs::REQ_MASK; switch (cmd) { /** @TODO(balazs.racz) need to validate caller of mutating @@ -450,7 +450,7 @@ struct TrainService::Impl ++nextConsistIndex_; return again(); } - uint8_t cmd = payload()[0]; + uint8_t cmd = payload()[0] & TractionDefs::REQ_MASK; bool flip_speed = false; if (cmd == TractionDefs::REQ_SET_SPEED) { if (flags & TractionDefs::CNSTFLAGS_REVERSE) { @@ -486,6 +486,7 @@ struct TrainService::Impl if (flip_speed) { b->data()->payload[1] ^= 0x80; } + b->data()->payload[0] |= TractionDefs::REQ_LISTENER; iface()->addressed_message_write_flow()->send(b); return exit(); } @@ -512,8 +513,11 @@ struct TrainService::Impl } b->data()->reset(message()->data()->mti, train_node()->node_id(), NodeHandle(dst), message()->data()->payload); - if ((payload()[0] == TractionDefs::REQ_SET_SPEED) && - (flags & TractionDefs::CNSTFLAGS_REVERSE)) { + b->data()->payload[0] |= TractionDefs::REQ_LISTENER; + if (((payload()[0] & TractionDefs::REQ_MASK) == + TractionDefs::REQ_SET_SPEED) && + (flags & TractionDefs::CNSTFLAGS_REVERSE)) + { b->data()->payload[1] ^= 0x80; } iface()->addressed_message_write_flow()->send(b);