18 #include <gtest/gtest.h> 29 #include <modbus/modbus.h> 32 #include <prbt_hardware_support/ModbusMsgInStamped.h> 40 #include <pilz_testutils/async_test.h> 45 using ::testing::AnyNumber;
46 using ::testing::AtLeast;
47 using ::testing::InSequence;
48 using ::testing::Invoke;
49 using ::testing::InvokeWithoutArgs;
50 using ::testing::Return;
51 using ::testing::Throw;
124 void SetUp()
override;
131 MOCK_METHOD1(modbus_read_cb,
void(
const ModbusMsgInStampedConstPtr& msg));
137 &PilzModbusClientTests::modbus_read_cb,
this);
143 return arg->holding_registers.data == vec;
147 return arg->disconnect.data;
157 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
175 EXPECT_CALL(*mock,
init(_, _)).Times(2).WillOnce(Return(
false)).WillOnce(Return(
true));
193 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
212 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
false));
230 EXPECT_CALL(*mock,
init(_, _)).Times(2).WillOnce(Return(
false)).WillOnce(Return(
false));
250 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
251 EXPECT_CALL(*mock, readHoldingRegister(_, _))
252 .WillOnce(Return(std::vector<uint16_t>{ 1, 2 }))
253 .WillOnce(Return(std::vector<uint16_t>{ 1, 2 }))
254 .WillOnce(Return(std::vector<uint16_t>{ 3, 4 }))
259 EXPECT_CALL(*
this, modbus_read_cb(IsSuccessfullRead(std::vector<uint16_t>{ 1, 2 }))).Times(2);
260 EXPECT_CALL(*
this, modbus_read_cb(IsSuccessfullRead(std::vector<uint16_t>{ 3, 4 }))).Times(1);
261 EXPECT_CALL(*
this, modbus_read_cb(IsDisconnect())).Times(1).WillOnce(ACTION_OPEN_BARRIER_VOID(
"disconnected"));
271 EXPECT_NO_THROW(client.run());
272 BARRIER(
"disconnected");
282 EXPECT_CALL(*mock,
init(_, _)).Times(0);
283 EXPECT_CALL(*mock, readHoldingRegister(_, _)).Times(0);
284 EXPECT_CALL(*
this, modbus_read_cb(_)).Times(0);
302 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
303 ON_CALL(*mock, readHoldingRegister(_, _)).WillByDefault(Return(std::vector<uint16_t>{ 3, 4 }));
304 ON_CALL(*
this, modbus_read_cb(IsSuccessfullRead(std::vector<uint16_t>{ 3, 4 }))).WillByDefault(Return());
309 auto client = std::make_shared<PilzModbusClient>(nh_, registers, std::move(mock),
RESPONSE_TIMEOUT,
317 EXPECT_TRUE(client->isRunning());
319 EXPECT_FALSE(client->isRunning());
333 const std::string topic_name{
"test_topic_name" };
338 auto client = std::make_shared<PilzModbusClient>(nh_, registers, std::move(mock),
RESPONSE_TIMEOUT, topic_name,
355 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
357 EXPECT_CALL(*mock, setResponseTimeoutInMs(response_timeout)).Times(1);
362 auto client = std::make_shared<PilzModbusClient>(nh_, registers, std::move(mock), response_timeout,
383 RegCont vec(static_cast<RegCont::size_type>(nb), 0u);
384 vec.at(register_index_) = curr_count_;
396 uint16_t curr_count_{ 0 };
402 void add(
const ModbusMsgInStampedConstPtr& msg)
404 std::unique_lock<std::mutex> lk(m_);
405 buffer_ = msg->holding_registers.data.at(0);
410 std::unique_lock<std::mutex> lk(m_);
436 std::vector<double> checked_frequencies{ 20.0, 40.0, 60.0 };
438 for (
const auto& expected_read_frequency : checked_frequencies)
442 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
445 ON_CALL(*mock, readHoldingRegister(_, _))
454 auto client = std::make_shared<PilzModbusClient>(
464 ASSERT_TRUE(client->isRunning());
469 auto final_value = buffer.
get();
474 auto actual_frequency = (final_value - 1) / (time_stop - time_start).toSec();
475 EXPECT_NEAR(expected_read_frequency, actual_frequency, 0.1 * expected_read_frequency);
477 EXPECT_FALSE(client->isRunning());
489 EXPECT_CALL(*mock,
init(_, _)).Times(1).WillOnce(Return(
true));
491 ON_CALL(*mock, readHoldingRegister(_, _)).WillByDefault(Return(std::vector<uint16_t>{ 1, 7 }));
494 RegCont expected_write_reg{ 1, 5, 4 };
495 WriteModbusRegister reg_write_srv;
496 reg_write_srv.request.holding_register_block.start_idx = 3;
497 reg_write_srv.request.holding_register_block.values = expected_write_reg;
499 RegCont expected_read_reg{ 14, 17 };
501 EXPECT_CALL(*mock, writeReadHoldingRegister(static_cast<int>(reg_write_srv.request.holding_register_block.start_idx),
503 static_cast<int>(expected_read_reg.size())))
505 .WillOnce(DoAll(ACTION_OPEN_BARRIER_VOID(
"writeHoldingRegister"), Return(expected_read_reg)));
507 EXPECT_CALL(*
this, modbus_read_cb(_)).Times(AnyNumber());
509 EXPECT_CALL(*
this, modbus_read_cb(IsSuccessfullRead(expected_read_reg))).Times(AtLeast(1));
511 std::vector<unsigned short> registers(expected_read_reg.size());
514 auto modbus_client = std::make_shared<PilzModbusClient>(nh_, registers, std::move(mock),
RESPONSE_TIMEOUT,
523 "not advertised in time";
527 EXPECT_TRUE(modbus_client->isRunning()) <<
"Modbus client not running";
529 EXPECT_TRUE(writer_client.call(reg_write_srv)) <<
"Modbus write service failed";
530 EXPECT_TRUE(reg_write_srv.response.success) <<
"Modbus write service could not write modbus register";
532 BARRIER(
"writeHoldingRegister");
535 EXPECT_FALSE(modbus_client->isRunning());
544 std::vector<unsigned short> in_throw = { 1, 2, 1 };
548 std::vector<unsigned short> in_split = { 1, 2, 4, 5 };
550 std::vector<std::vector<unsigned short>> expected_out_split = { { 1, 2 }, { 4, 5 } };
551 EXPECT_EQ(out_split, expected_out_split);
554 std::vector<unsigned short> in_nosplit = { 1, 2, 3 };
556 std::vector<std::vector<unsigned short>> expected_out_nosplit = { { 1, 2, 3 } };
557 EXPECT_EQ(out_nosplit, expected_out_nosplit);
561 int main(
int argc,
char* argv[])
563 ros::init(argc, argv,
"pilz_modbus_client_test");
566 testing::InitGoogleTest(&argc, argv);
567 return RUN_ALL_TESTS();
HoldingRegisterIncreaser(unsigned int register_index=0u)
void run()
Publishes the register values as messages.
ros::Subscriber subscriber_
std::vector< uint16_t > RegCont
Convenience data type defining the data type for a collection of registers.
int main(int argc, char *argv[])
void add(const ModbusMsgInStampedConstPtr &msg)
Expection thrown by prbt_hardware_support::PilzModbusClient.
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
static constexpr unsigned int REGISTER_FIRST_IDX_TEST
void init(const M_string &remappings)
void callbackDummy(const ModbusMsgInStampedConstPtr &)
PilzModbusClientExecutor(PilzModbusClient *client)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
uint32_t getNumPublishers() const
static const std::string SERVICE_MODBUS_WRITE
std::thread client_thread_
static std::vector< std::vector< unsigned short > > splitIntoBlocks(std::vector< unsigned short > &in)
Splits a vector of integers into a vector of vectors with consecutive groups.
static constexpr double WAIT_FOR_START_TIMEOUT_S
static constexpr unsigned int REGISTER_SIZE_TEST
MATCHER(IsDisconnect, "")
bool isRunning()
True if 'run()' method is active, false if 'run()' method is not active or, currently, finishing.
static constexpr double WAIT_SLEEPTIME_S
Test if PilzModbusClient correctly publishes ROS-Modbus messages.
static const std::string TOPIC_MODBUS_READ
static constexpr double WAIT_FOR_STOP_TIMEOUT_S
static constexpr double WAIT_FOR_SERVICE_TIMEOUT_S
Mock used in the unittest of the PilzModbusClient.
TEST_F(LibModbusClientTest, testModbusClientDtor)
Test increases function coverage by ensuring that all Dtor variants are called.
Connects to a modbus server and publishes the received data into ROS.
Strictly increases the holding register each time the "readHoldingRegister" method is called...
PilzModbusClient * client_
Helper class to simplify the threading of the PilzModbusClient.
MATCHER_P(IsSuccessfullRead, vec, "")
static constexpr unsigned int DEFAULT_MODBUS_PORT_TEST
const unsigned int register_index_
RegCont readHoldingRegister(int, int nb)
constexpr const char *const LOCALHOST
Expection thrown by prbt_hardware_support::LibModbusClient::readHoldingRegister if a disconnect from ...
void terminate()
Ends the infinite loop started in method 'run()'.
static constexpr unsigned int RESPONSE_TIMEOUT