Skip to content

Commit

Permalink
io modbus create methods for restarting in thread
Browse files Browse the repository at this point in the history
  • Loading branch information
DaAwesomeP committed Feb 22, 2023
1 parent 0635e12 commit 12cd6cd
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 13 deletions.
21 changes: 20 additions & 1 deletion include/aniray/IOInterfaceModbus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ struct ConfigInputDiscrete {
// std::uint16_t clearNumAddressedItems;
};

enum class InitializeOptionsMode {
TCP
};

struct InitializeOptions {
InitializeOptionsMode mode;
std::string tcpAddress;
std::uint16_t tcpPort;
};

// Not enum to enforce strong typing
const std::uint8_t FUNCTION_CODE_READ_BITS = 1;
const std::uint8_t FUNCTION_CODE_READ_INPUT_BITS = 2;
Expand Down Expand Up @@ -94,6 +104,7 @@ class IOInterfaceModbus : public aniray::IOInterface::IOInterfaceGeneric {
auto operator=(IOInterfaceModbus&&) noexcept -> IOInterfaceModbus& = delete; // move assignment

void refreshInputs() override;
void reconnect();
void setupInputDiscrete(const std::string &name,
std::uint8_t slaveID,
std::uint8_t functionCode,
Expand All @@ -110,12 +121,20 @@ class IOInterfaceModbus : public aniray::IOInterface::IOInterfaceGeneric {
std::uint16_t clearStartAddress,
bool onlyClearSingleAddress,
bool clearHigh);
[[nodiscard]] auto modbusInitialized() const -> bool;

private:
std::unordered_map<std::string, ConfigInputDiscrete> mInputsDiscreteModbus;
mutable std::shared_mutex mMutexModbusInitialize;
mutable std::shared_mutex mMutexInputsDiscreteModbus;
modbus_t *mCTX;
bool mModbusInitialized = false;
InitializeOptions mInitializeOptions;

void setupConnectionTCP(std::string tcpAddress, std::uint16_t tcpPort);
void initialize();
void initConnectionTCP(std::string tcpAddress, std::uint16_t tcpPort);
void deInitializeNoLock();
void deInitialize();
void updateInputDiscreteModbusRead(const ConfigInputDiscrete &configInputDiscrete,
std::vector<std::uint8_t> &dest8,
std::vector<std::uint16_t> &dest16);
Expand Down
1 change: 1 addition & 0 deletions include/aniray/PeriodicThread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class PeriodicThread { // NOLINT(cppcoreguidelines-special-member-functions,hicp
protected:
virtual void periodicAction() = 0;
private:
bool mWasRunning = false;
bool mRunning = false;
std::chrono::milliseconds mUpdateRateMs;
boost::asio::io_context mIOContext;
Expand Down
83 changes: 73 additions & 10 deletions src/IOInterfaceModbus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,74 @@
namespace aniray::IOInterface::Modbus {

IOInterfaceModbus::IOInterfaceModbus(std::string tcpAddress, std::uint16_t tcpPort) {
setupConnectionTCP(std::move(tcpAddress), tcpPort);
mInitializeOptions = {
.mode = InitializeOptionsMode::TCP,
.tcpAddress = std::move(tcpAddress),
.tcpPort = tcpPort
};
initialize();

// Refresh inputs to initially populate values and confirm all addresses are reachable
// refreshInputs();
}

IOInterfaceModbus::~IOInterfaceModbus() {
modbus_close(mCTX);
modbus_free(mCTX);
// Intentionally no lock, never block destructor
deInitialize();
}
// NOTE: This method relies on lock from calling function (or none if constructor)!
void IOInterfaceModbus::setupConnectionTCP(std::string tcpAddress, std::uint16_t tcpPort) {

[[nodiscard]] auto IOInterfaceModbus::modbusInitialized() const -> bool {
const std::shared_lock<std::shared_mutex> lock(mMutexModbusInitialize);
return mModbusInitialized;
}

// NOTE: This method relies on lock from calling function !
void IOInterfaceModbus::deInitializeNoLock() {
if (mCTX != nullptr) {
if (mModbusInitialized) {
modbus_close(mCTX);
modbus_free(mCTX);
mModbusInitialized = false;
}
}
}

// NOTE: This method relies on lock from calling function !
void IOInterfaceModbus::deInitialize() {
const std::unique_lock<std::shared_mutex> lock(mMutexModbusInitialize);
deInitializeNoLock();
}


void IOInterfaceModbus::reconnect() {
deInitialize();
initialize();
}

void IOInterfaceModbus::initialize() {
switch (mInitializeOptions.mode) {
case InitializeOptionsMode::TCP:
initConnectionTCP(mInitializeOptions.tcpAddress, mInitializeOptions.tcpPort);
break;
default:
throw std::runtime_error("IOInterfaceModbus: Unknown initialize options mode!");
break;
}
}

void IOInterfaceModbus::initConnectionTCP(std::string tcpAddress, std::uint16_t tcpPort) {
const std::unique_lock<std::shared_mutex> lock(mMutexModbusInitialize);
mCTX = modbus_new_tcp_pi(tcpAddress.c_str(), std::to_string(tcpPort).c_str());
if (mCTX == nullptr) {
mModbusInitialized = false;
throw std::runtime_error("IOInterfaceModbus: Unable to allocate libmodbus context");
}
if (modbus_connect(mCTX) == -1) {
mModbusInitialized = false;
modbus_free(mCTX);
throw std::runtime_error("IOInterfaceModbus: Connection failed: " + std::string(modbus_strerror(errno)));
}
mModbusInitialized = true;
BOOST_LOG_TRIVIAL(info) << "IOInterfaceModbus: Connected to "
<< tcpAddress << ":" << tcpPort;
}
Expand Down Expand Up @@ -130,11 +178,14 @@ void IOInterfaceModbus::setupInputDiscrete(const std::string &name,
}

void IOInterfaceModbus::refreshInputs() {
const std::shared_lock<std::shared_mutex> initializeLock(mMutexModbusInitialize);
const std::shared_lock<std::shared_mutex> inputsDiscreteLock(mMutexInputsDiscreteModbus);
for (auto const& [name, configInputDiscrete] : mInputsDiscreteModbus) {
updateInputDiscrete(configInputDiscrete);
if (mModbusInitialized) {
for (auto const& [name, configInputDiscrete] : mInputsDiscreteModbus) {
updateInputDiscrete(configInputDiscrete);
}
// inputsDiscreteLock.unlock(); // will need this when other types of input are added
}
// inputsDiscreteLock.unlock(); // will need this when other types of input are added
}

// NOTE: This method relies on lock from calling function!
Expand Down Expand Up @@ -329,15 +380,27 @@ void IOInterfaceModbusPeriodicThread::periodicAction() {
try {
refreshInputs();
} catch (const std::exception &e) {
stop();
mRefreshError = true;
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Refresh error: " << e.what();
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Stopping due to error";
stop();
} catch (...) {
stop();
mRefreshError = true;
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Refresh error: Unknown";
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Stopping due to error";
stop();
}
while (mRefreshError || !running() || !modbusInitialized()) {
BOOST_LOG_TRIVIAL(info) << "IOInterfaceModbusPeriodicThread: Retrying connection";
try {
reconnect();
start();
mRefreshError = false;
} catch (const std::exception &e) {
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Retrying connection error: " << e.what();
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "IOInterfaceModbusPeriodicThread: Retrying connection error: Unknown";
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions src/PeriodicThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
*/

#include <chrono>
// #include <iostream>
#include <memory>
#include <mutex>
#include <shared_mutex>
Expand Down Expand Up @@ -53,8 +52,13 @@ PeriodicThread::PeriodicThread(std::chrono::milliseconds updateRateMs)
}

void PeriodicThread::start() {
if (mWasRunning) {
mIOContext.restart();
} else {
mIOThread = std::make_unique<boost::thread>([ObjectPtr = &mIOContext] { ObjectPtr->run(); });
}
mWasRunning = true;
mRunning = true;
mIOThread = std::make_unique<boost::thread>([ObjectPtr = &mIOContext] { ObjectPtr->run(); });
}

void PeriodicThread::stop() {
Expand Down

0 comments on commit 12cd6cd

Please sign in to comment.