Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #88

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions include/hnz.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,15 @@ class HNZ {

/**
* Start the HZN south plugin
* @param requestedStart tells if start was requested by the Fledge API
*/
void start();
void start(bool requestedStart = false);

/**
* Stop the HZN south plugin
* @param requestedStop tells if stop was requested by the Fledge API
*/
void stop();
void stop(bool requestedStop = false);

/**
* Save the callback function and its data
Expand Down Expand Up @@ -170,7 +172,11 @@ class HNZ {
void sendInitialGI();

private:
// Tells if the plugin is currently running
std::atomic<bool> m_is_running{false};
// Tells if the plugin should be running (eg: if you should be restarted after a configuration change)
std::atomic<bool> m_should_run{false};

// Receiving threads
std::unique_ptr<std::thread> m_receiving_thread_A;
std::unique_ptr<std::thread> m_receiving_thread_B;
Expand Down
23 changes: 18 additions & 5 deletions src/hnz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ HNZ::HNZ() {}

HNZ::~HNZ() {
if (m_is_running) {
stop();
stop(true);
}
}

void HNZ::start() {
void HNZ::start(bool requestedStart /*= false*/) {
std::lock_guard<std::recursive_mutex> guard(m_configMutex);
std::string beforeLog = HnzUtility::NamePlugin + " - HNZ::start -";

if (requestedStart) {
m_should_run = true;
}

if (m_is_running) {
HnzUtility::log_info("%s HNZ south plugin already started", beforeLog.c_str());
return;
}

if (!m_hnz_conf->is_complete()) {
HnzUtility::log_info("%s HNZ south plugin can't start because configuration is incorrect.", beforeLog.c_str());
return;
Expand All @@ -53,11 +62,15 @@ void HNZ::start() {
m_hnz_connection->start();
}

void HNZ::stop() {
void HNZ::stop(bool requestedStop /*= false*/) {
std::string beforeLog = HnzUtility::NamePlugin + " - HNZ::stop -";
HnzUtility::log_info("%s Starting shutdown of HNZ plugin", beforeLog.c_str());
m_is_running = false;

if (requestedStop) {
m_should_run = false;
}

// Connection must be stopped before management threads of both path
// or join on both receive threads will hang forever
if (m_hnz_connection != nullptr) {
Expand Down Expand Up @@ -104,7 +117,7 @@ void HNZ::setJsonConfig(const string& protocol_conf_json, const string& msg_conf
HnzUtility::log_info("%s No new configuration provided to reconfigure, skipping", beforeLog.c_str());
return;
}
bool was_running = m_is_running;

if (m_is_running) {
HnzUtility::log_info("%s Configuration change requested, stopping the plugin", beforeLog.c_str());
stop();
Expand All @@ -129,7 +142,7 @@ void HNZ::setJsonConfig(const string& protocol_conf_json, const string& msg_conf
m_test_msg_receive = m_hnz_conf->get_test_msg_receive();
m_hnz_connection = make_unique<HNZConnection>(m_hnz_conf, this);

if (was_running) {
if (m_should_run) {
HnzUtility::log_warn("%s Restarting the plugin...", beforeLog.c_str());
start();
}
Expand Down
2 changes: 1 addition & 1 deletion src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ void plugin_start(PLUGIN_HANDLE *handle) {
std::string beforeLog = HnzUtility::NamePlugin + " - plugin_start -";
HnzUtility::log_info("%s Starting the plugin...", beforeLog.c_str());
auto hnz = reinterpret_cast<HNZ *>(handle);
hnz->start();
hnz->start(true);
HnzUtility::log_info("%s Plugin started", beforeLog.c_str());
}

Expand Down
111 changes: 106 additions & 5 deletions tests/test_hnz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,13 @@ class HNZTest : public testing::Test {
const std::string& protocol_stack = protocol_stack_generator(port, port2);
std::string configure = std::regex_replace(configureTemplate, std::regex("<protocol_stack>"), protocol_stack);
configure = std::regex_replace(configure, std::regex("<exchanged_data>"), exchanged_data_def);
auto config = new ConfigCategory("newConfig", configure);
hnz->reconfigure(*config);
ConfigCategory config("newConfig", configure);
hnz->reconfigure(config);
}

static void startHNZ(int port, int port2) {
initConfig(port, port2);
hnz->start();
hnz->start(true);
}

template<class... Args>
Expand Down Expand Up @@ -661,13 +661,13 @@ class ServersWrapper {
}
std::shared_ptr<BasicHNZServer> server1() {
if (m_server1 && !m_server1->HNZServerIsReady()) {
m_server1 = nullptr;
return nullptr;
}
return m_server1;
}
std::shared_ptr<BasicHNZServer> server2() {
if (m_server2 && !m_server2->HNZServerIsReady()) {
m_server2 = nullptr;
return nullptr;
}
return m_server2;
}
Expand Down Expand Up @@ -2155,6 +2155,107 @@ TEST_F(HNZTest, ReconfigureWhileConnectionActive) {
if(HasFatalFailure()) return;
}

TEST_F(HNZTest, ReconfigureBadConfig) {
ServersWrapper wrapper(0x05, getNextPort());
BasicHNZServer* server = wrapper.server1().get();
ASSERT_NE(server, nullptr) << "Something went wrong. Connection is not established in 10s...";
validateAllTIQualityUpdate(true, false);
if(HasFatalFailure()) return;

debug_print("[HNZ south plugin] Send bad plugin configuration");
clearReadings();
static const std::string badConfig = QUOTE({
"exchanged_data" : {
"value": 42
}
});
ConfigCategory config("newConfig", badConfig);
hnz->reconfigure(config);

// Check that connection was lost
waitUntil(southEventsReceived, 1, 1000);
// Check that ingestCallback had been called only one time
ASSERT_EQ(southEventsReceived, 1);
// Validate new connection state
std::shared_ptr<Reading> currentReading = popFrontReadingsUntil("TEST_STATUS");
validateSouthEvent(currentReading, "TEST_STATUS", {
{"connx_status", "not connected"},
});
if(HasFatalFailure()) return;

// No quality update message as new config contains no TI
ASSERT_EQ(dataObjectsReceived, 0);

// Also stop the server as it is unable to reconnect on the fly
debug_print("[HNZ server] Request server stop...");
server->stopHNZServer();
debug_print("[HNZ server] Request server start...");
server->startHNZServer();

// Check that connection cannot be established as client is no longer running due to invalid configuration
BasicHNZServer* deadServer = wrapper.server1().get();
ASSERT_EQ(deadServer, nullptr) << "Something went wrong. Server should not be able to reconnect, but it did!";

// This calls HNZ::reconfigure() again, causing a reconnect of the client
debug_print("[HNZ south plugin] Send good plugin configuration");
clearReadings();
wrapper.initHNZPlugin();

// Check that connection attempt to reopen on client side
validateAllTIQualityUpdate(true, false, true);
if(HasFatalFailure()) return;

// Restart the server
debug_print("[HNZ server] Request server stop 2...");
server->stopHNZServer();
debug_print("[HNZ server] Request server start 2...");
server->startHNZServer();

// Check that the server is reconnected after reconfigure
server = wrapper.server1().get();
ASSERT_NE(server, nullptr) << "Something went wrong. Connection 2 is not established in 10s...";
// Wait for initial CG request
waitUntil(southEventsReceived, 2, 1000);
// Check that ingestCallback had been called only for two GI status updates
ASSERT_EQ(southEventsReceived, 2);
resetCounters();
// Validate reconnection
currentReading = popFrontReadingsUntil("TEST_STATUS");
validateSouthEvent(currentReading, "TEST_STATUS", {
{"connx_status", "started"},
});
if(HasFatalFailure()) return;
// Validate new GI state
currentReading = popFrontReadingsUntil("TEST_STATUS");
validateSouthEvent(currentReading, "TEST_STATUS", {
{"gi_status", "started"},
});
if(HasFatalFailure()) return;

// Complete CG request by sending all expected TS
server->sendFrame({0x16, 0x33, 0x00, 0x00, 0x00, 0x00}, false);
server->sendFrame({0x16, 0x39, 0x00, 0x02, 0x00, 0x00}, false);
debug_print("[HNZ Server] TSCG sent");
waitUntil(southEventsReceived, 2, 1000);
// Check that ingestCallback had been called only for two GI status updates
ASSERT_EQ(southEventsReceived, 2);
resetCounters();
currentReading = popFrontReadingsUntil("TEST_STATUS");
validateSouthEvent(currentReading, "TEST_STATUS", {
{"gi_status", "in progress"},
});
if(HasFatalFailure()) return;
currentReading = popFrontReadingsUntil("TEST_STATUS");
validateSouthEvent(currentReading, "TEST_STATUS", {
{"gi_status", "finished"},
});
if(HasFatalFailure()) return;

// Manually stop the server here or we may end up in a deadlock in the HNZServer
debug_print("[HNZ server] Request server stop 3...");
server->stopHNZServer();
}

TEST_F(HNZTest, UnknownMessage) {
ServersWrapper wrapper(0x05, getNextPort());
BasicHNZServer* server = wrapper.server1().get();
Expand Down
Loading