Skip to content

Commit

Permalink
WIP modbus IO
Browse files Browse the repository at this point in the history
  • Loading branch information
DaAwesomeP committed Feb 11, 2023
1 parent 068b827 commit 6f8844c
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 147 deletions.
9 changes: 3 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ add_library(
include/aniray/DMXAddr.hpp
include/aniray/Geometry.hpp
include/aniray/IOInterface.hpp
include/aniray/IOInterfaceModbus.hpp
include/aniray/Node.hpp
include/aniray/NodeAnimation.hpp
include/aniray/NodeArray.hpp
Expand Down Expand Up @@ -85,7 +84,9 @@ if(NOT ANIRAY_WITHOUT_OLA)
endif()
if(NOT ANIRAY_WITHOUT_MODBUS)
# Private to avoid double linking https://stackoverflow.com/a/34533415
target_sources(${CMAKE_PROJECT_NAME} PRIVATE include/aniray/IOInterfaceModbus.hpp)
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
include/aniray/IOInterfaceModbus.hpp
src/IOInterfaceModbus.cpp)
endif()

if(ANIRAY_WITH_LINT)
Expand All @@ -97,10 +98,6 @@ if(ANIRAY_WITH_LINT)
src/lintHelpers/NodeArray.cpp
src/lintHelpers/NodeArrayOutput.cpp
src/lintHelpers/NodeArraySampler.cpp)
if(NOT ANIRAY_WITHOUT_MODBUS)
# Private to avoid double linking https://stackoverflow.com/a/34533415
target_sources(${CMAKE_PROJECT_NAME} PRIVATE src/lintHelpers/IOInterfaceModbus.cpp)
endif()
set_target_properties(
${CMAKE_PROJECT_NAME}
PROPERTIES
Expand Down
4 changes: 2 additions & 2 deletions include/aniray/IOInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ class IOInterfaceInput {
// https://en.cppreference.com/w/cpp/container/vector_bool
using IOInterfaceInputDiscrete = IOInterfaceInput<bool>;

class IOInterface {
class IOInterfaceGeneric {
public:
[[nodiscard]] auto getInputsDiscrete(const std::string &name) const -> std::shared_ptr<IOInterfaceInputDiscrete>;
protected:
virtual void refreshInputs() = 0;
protected:
void assignInputDiscrete(const std::string &name, std::shared_ptr<IOInterfaceInputDiscrete> input);
private:
std::unordered_map<std::string, std::shared_ptr<IOInterfaceInputDiscrete>> mInputsDiscrete;
Expand Down
142 changes: 33 additions & 109 deletions include/aniray/IOInterfaceModbus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,132 +38,56 @@

namespace aniray {
namespace IOInterface {
namespace IOInterfaceModbus {
namespace Modbus {

enum ConfigType {
CONFIG_TYPE_TCP
enum class ConfigFunctionsAddressLayout {
ADDRESS, // each address is an input
BITS_LSB, // each bit is an input, first bit is first address
SPAN_2_LSB // 16- or 32-bit value over two addresses, LSB
};
struct Config {
ConfigType type;
std::string address;
std::uint16_t port;
std::vector<IOInterfaceModbusConfigFunctions> functions;
};
enum ConfigFunctionsType {
CONFIG_FUNCTIONS_TYPE_INPUT_DISCRETE
// CONFIG_FUNCTIONS_TYPE_INPUT_COUNTER
};
struct ConfigFunction {
struct ConfigInputDiscrete {
std::string name;
ConfigFunctionsType type;
std::uint8_t slaveID;
std::uint8_t functionCode;
ConfigFunctionsAddressLayout addressLayout;
std::uint16_t startAddress;
std::uint16_t numItems;
// bool clearCounterEnable;
// std::uint8_t clearCounterFunctionCode;
// std::uint16_t clearCounterAddress;
std::uint16_t numAddressedItems;
bool enableClear;
std::uint8_t clearFunctionCode;
bool clearSingleAddress;
std::uint16_t clearAddress;
// std::uint16_t clearNumAddressedItems;
};

// 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;
const std::uint8_t FUNCTION_CODE_READ_REGISTERS = 3;
const std::uint8_t FUNCTION_CODE_READ_INPUT_REGISTERS = 4;
const std::uint8_t FUNCTION_CODE_FORCE_SINGLE_COIL = 5;

class IOInterfaceModbus : public aniray::IOInterface::IOInterface {
class IOInterfaceModbus : public aniray::IOInterface::IOInterfaceGeneric {
public:
IOInterfaceModbus(Config config)
: mConfig(config) {
switch (mConfig.type) {
case CONFIG_TYPE_TCP:
setupConnectionTCP();
break;
default:
throw std::runtime_error("IOInterfaceModbus: Unknown connection type!");
break;
}

for (ConfigFunction functionConfig : mConfig.functions) {
switch (functionConfig.type) {
case CONFIG_FUNCTIONS_TYPE_INPUT_DISCRETE:
setupInputsDiscrete(functionConfig);
break;
// case CONFIG_FUNCTIONS_TYPE_INPUT_COUNTER:
// setupCounter(functionConfig);
// break;
default:
throw std::runtime_error("IOInterfaceModbus: Unknown function type!");
break;
}
}

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

~IOInterfaceModbus() {
modbus_close(mCTX);
modbus_free(mCTX);
}
IOInterfaceModbus(std::string tcpAddress, std::uint16_t tcpPort);
~IOInterfaceModbus();
void refreshInputs() override;
void setupInputDiscrete(std::string name,
std::uint8_t slaveID,
std::uint8_t functionCode,
ConfigFunctionsAddressLayout addressLayout,
std::uint16_t startAddress,
std::uint16_t numAddressedItems,
bool enableClear = false,
std::uint8_t clearFunctionCode = FUNCTION_CODE_FORCE_SINGLE_COIL,
bool clearSingleAddress = false,
std::uint16_t clearAddress = 0);
private:
Config mConfig;
std::unordered_map<std::string, ConfigInputDiscrete> mInputsDiscreteModbus;
mutable std::shared_mutex mMutexInputsDiscreteModbus;
modbus_t *mCTX;

void setupConnectionTCP() {
mCTX = modbus_new_tcp_pi(mConfig.address.c_str(), std::to_string(mConfig.port).c_str());
if (mCTX == NULL) {
throw std::runtime_error("IOInterfaceModbus: Unable to allocate libmodbus context");
} else if (modbus_connect(mCTX) == -1) {
modbus_free(mCTX);
throw std::runtime_error("IOInterfaceModbus: Connection failed: " + std::string(modbus_strerror(errno)));
}
BOOST_LOG_TRIVIAL(info) << "IOInterfaceModbus: Connected to "
<< mConfig.address << ":" << mConfig.port;
}

void setupInputsDiscrete(ConfigFunction functionConfig) {
mInputsDiscrete.assignInputDiscrete(functionConfig.name, std::make_shared<IOInterfaceInputDiscrete>());
}

void refreshInputs() override {
for (ConfigFunction functionConfig : mConfig.functions) {
switch (functionConfig.type) {
case CONFIG_FUNCTIONS_TYPE_INPUT_DISCRETE:
updateInputsDiscrete(functionConfig);
break;
// case CONFIG_FUNCTIONS_TYPE_INPUT_COUNTER:
// updateInputsCounter(functionConfig);
// break;
default:
throw std::runtime_error("IOInterfaceModbus: Unknown function type!");
break;
}
}
}

void updateInputsDiscrete(ConfigFunction functionConfig) {
if (modbus_set_slave(mCTX, functionConfig.slaveID) == -1) {
throw std::runtime_error("IOInterfaceModbus: Invalid slave ID: " + std::to_string(functionConfig.slaveID));
}
switch (functionConfig.functionCode) {
case FUNCTION_CODE_READ_BITS:
std::vector<int> dest(functionConfig.numItems);
modbus_read_bits(mCTX, functionConfig.startAddress, functionConfig.numItems);
mInputsDiscrete[functionConfig.name].setValues(&dest[0]);
break;

case FUNCTION_CODE_READ_INPUT_BITS:
std::vector<int> dest(functionConfig.numItems);
modbus_read_input_bits(mCTX, functionConfig.startAddress, functionConfig.numItems);
mInputsDiscrete[functionConfig.name].setValues(&dest[0]);
break;

default:
throw std::runtime_error("IOInterfaceModbus: Incorrect discrete input function code!");
break;

}
}
void setupConnectionTCP(std::string tcpAddress, std::uint16_t tcpPort);
void updateInputDiscrete(ConfigInputDiscrete configInputDiscrete);

// void updateInputsCounter(ConfigFunction functionConfig) {}
};
Expand Down
6 changes: 3 additions & 3 deletions src/IOInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@

namespace aniray::IOInterface {

auto IOInterface::getInputsDiscrete(const std::string &name) const -> std::shared_ptr<IOInterfaceInputDiscrete> {
auto IOInterfaceGeneric::getInputsDiscrete(const std::string &name) const -> std::shared_ptr<IOInterfaceInputDiscrete> {
return mInputsDiscrete.at(name);
}

void IOInterface::assignInputDiscrete(const std::string &name, std::shared_ptr<IOInterfaceInputDiscrete> input) {
void IOInterfaceGeneric::assignInputDiscrete(const std::string &name, std::shared_ptr<IOInterfaceInputDiscrete> input) {
if (mInputsDiscrete.count(name) > 0) {
throw std::runtime_error("IOInterfaceModbus: Duplicate discrete input! Name: " + name);
throw std::runtime_error("IOInterfaceGeneric: Duplicate discrete input! Name: " + name);
}
mInputsDiscrete[name] = std::move(input);
}
Expand Down
Loading

0 comments on commit 6f8844c

Please sign in to comment.