Skip to content

Commit

Permalink
Converted 'data_publish' example to use C++17 std::filesystem
Browse files Browse the repository at this point in the history
  • Loading branch information
fpagliughi committed Jul 5, 2024
1 parent be26343 commit 33d219a
Showing 1 changed file with 41 additions and 67 deletions.
108 changes: 41 additions & 67 deletions examples/data_publish.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
//

/*******************************************************************************
* Copyright (c) 2013-2023 Frank Pagliughi <fpagliughi@mindspring.com>
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
Expand All @@ -41,15 +41,6 @@
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/

#if !defined(_WIN32)
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <fstream>
#endif

#include <algorithm>
#include <chrono>
#include <cstdlib>
Expand All @@ -59,30 +50,34 @@
#include <random>
#include <string>
#include <thread>
#include <filesystem>
#include <fstream>

#include "mqtt/async_client.h"

using namespace std;
using namespace std::chrono;
namespace fs = std::filesystem;

const std::string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const std::string CLIENT_ID{"paho-cpp-data-publish"};

const string TOPIC{"data/rand"};
const int QOS = 1;

// How often we output a data point
const auto PERIOD = seconds(5);

// The number of out-bound messages we will buffer locally when disconnected.
const int MAX_BUFFERED_MSGS = 120; // 120 * 5sec => 10min off-line buffering

const string PERSIST_DIR{"data-persist"};

/////////////////////////////////////////////////////////////////////////////
// Top-level directory to keep persistence data
const fs::path PERSIST_DIR{"persist"};

// At some point, when the library gets updated to C++17, we can use
// std::filesystem to make a portable version of this.
// A key for encoding the persistence data
const string PERSIST_KEY{"elephant"};

#if !defined(_WIN32)
/////////////////////////////////////////////////////////////////////////////

// Example of user-based file persistence with a simple XOR encoding scheme.
//
Expand All @@ -96,11 +91,10 @@ const string PERSIST_DIR{"data-persist"};
// SQLite or a local key/value store like Redis.
class encoded_file_persistence : virtual public mqtt::iclient_persistence
{
// The name of the store
// Used as the directory name
string name_;
// The directory for the persistence store.
fs::path dir_;

// A key for encoding the data
// A key for encoding the data, as supplied by the user
string encodeKey_;

// Simple, in-place XOR encoding and decoding
Expand All @@ -114,7 +108,7 @@ class encoded_file_persistence : virtual public mqtt::iclient_persistence
}

// Gets the persistence file name for the supplied key.
string path_name(const string& key) const { return name_ + "/" + key; }
fs::path path_name(const string& key) const { return dir_ /key; }

public:
// Create the persistence object with the specified encoding key
Expand All @@ -132,52 +126,43 @@ class encoded_file_persistence : virtual public mqtt::iclient_persistence
if (clientId.empty() || serverURI.empty())
throw mqtt::persistence_exception();

name_ = serverURI + "-" + clientId;
std::replace(name_.begin(), name_.end(), ':', '-');
// Create a name for the persistence subdirectory for this client
string name = serverURI + "-" + clientId;
std::replace(name.begin(), name.end(), ':', '-');

dir_ = PERSIST_DIR;
dir_ /= name;

mkdir(name_.c_str(), S_IRWXU | S_IRWXG);
fs::create_directories(dir_);
}

// Close the persistent store that was previously opened.
// Remove the persistence directory, if it's empty.
void close() override { rmdir(name_.c_str()); }
void close() override { fs::remove(dir_); }

// Clears persistence, so that it no longer contains any persisted data.
// Just remove all the files from the persistence directory.
void clear() override
{
DIR* dir = opendir(name_.c_str());
if (!dir)
return;

dirent* next;
while ((next = readdir(dir)) != nullptr) {
auto fname = string(next->d_name);
if (fname == "." || fname == "..")
continue;
string path = name_ + "/" + fname;
remove(path.c_str());
// We could iterate through and remove each file,
// but this does the same thing in fewer steps.
if (!fs::is_empty(dir_)) {
fs::remove_all(dir_);
fs::create_directories(dir_);
}
closedir(dir);
}

// Returns whether or not data is persisted using the specified key.
// We just look for a file in the store directory with the same name as
// the key.
bool contains_key(const string& key) override
{
DIR* dir = opendir(name_.c_str());
if (!dir)
return false;

dirent* next;
while ((next = readdir(dir)) != nullptr) {
if (string(next->d_name) == key) {
closedir(dir);
return true;
if (fs::exists(dir_)) {
for (const auto& entry : fs::directory_iterator(dir_)) {
if (entry.path().filename() == key)
return true;
}
}
closedir(dir);
return false;
}

Expand All @@ -186,19 +171,12 @@ class encoded_file_persistence : virtual public mqtt::iclient_persistence
mqtt::string_collection keys() const override
{
mqtt::string_collection ks;
DIR* dir = opendir(name_.c_str());
if (!dir)
return ks;

dirent* next;
while ((next = readdir(dir)) != nullptr) {
auto fname = string(next->d_name);
if (fname == "." || fname == "..")
continue;
ks.push_back(fname);
}

closedir(dir);
if (fs::exists(dir_)) {
for (const auto& entry : fs::directory_iterator(dir_)) {
ks.push_back(entry.path().filename().string());
}
}
return ks;
}

Expand Down Expand Up @@ -255,27 +233,23 @@ class encoded_file_persistence : virtual public mqtt::iclient_persistence
void remove(const string& key) override
{
auto path = path_name(key);
::remove(path.c_str());
fs::remove(path);
}
};
#endif

/////////////////////////////////////////////////////////////////////////////

int main(int argc, char* argv[])
{
string serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;

#if defined(_WIN32)
mqtt::async_client cli(serverURI, CLIENT_ID, MAX_BUFFERED_MSGS);
#else
encoded_file_persistence persist("elephant");
encoded_file_persistence persist{PERSIST_KEY};

mqtt::async_client cli(serverURI, CLIENT_ID, MAX_BUFFERED_MSGS, &persist);
#endif

auto connOpts = mqtt::connect_options_builder()
.keep_alive_interval(MAX_BUFFERED_MSGS * PERIOD)
.clean_session(true)
.clean_session(false)
.automatic_reconnect(true)
.finalize();

Expand Down

0 comments on commit 33d219a

Please sign in to comment.