Skip to content

Commit

Permalink
Added callback support
Browse files Browse the repository at this point in the history
  • Loading branch information
wardru committed Sep 25, 2023
1 parent 44144e3 commit 98a16da
Show file tree
Hide file tree
Showing 16 changed files with 216 additions and 235 deletions.
47 changes: 31 additions & 16 deletions examples/bps.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

int main(void)
{
pza::core::set_log_level(pza::core::log_level::trace);
pza::core::set_log_level(pza::core::log_level::debug);

pza::client::ptr cli = std::make_shared<pza::client>("localhost", 1883);

Expand All @@ -18,25 +18,40 @@ int main(void)
return -1;

for (size_t i = 0; i < bps->get_num_channels(); i++) {
auto bps_channel = bps->channel[i];

auto mdr = [&](double value) {
spdlog::info("Voltage: {}", value);
};

bps_channel->voltmeter.add_measure_callback(mdr);

bps_channel->voltmeter.remove_measure_callback(mdr);

auto lol = [&](double value) {
spdlog::info("double: {}", value);
};

bps_channel->voltmeter.add_measure_callback(lol);

bps_channel->voltmeter.remove_measure_callback(lol);

spdlog::info("Channel {}:", i);
bps->channel[i]->set_voltage(-7.3);
bps->channel[i]->set_current(1.0);
bps->channel[i]->set_enable(false);
spdlog::info(" Voltage: {}", bps->channel[i]->get_voltage());
spdlog::info(" Current: {}", bps->channel[i]->get_current());
spdlog::info(" Enable: {}", bps->channel[i]->get_enable());
bps->channel[i]->set_voltage(3.3);
bps->channel[i]->set_current(5.0);
bps->channel[i]->set_enable(true);
spdlog::info(" Voltage: {}", bps->channel[i]->get_voltage());
spdlog::info(" Current: {}", bps->channel[i]->get_current());
spdlog::info(" Enable: {}", bps->channel[i]->get_enable());
bps_channel->ctrl.set_voltage(-7.3);
bps_channel->ctrl.set_current(1.0);
bps_channel->ctrl.set_enable(false);
spdlog::info(" Voltage: {}", bps_channel->voltmeter.get_measure());
spdlog::info(" Current: {}", bps_channel->ampermeter.get_measure());
spdlog::info(" Enabled: {}", bps_channel->ctrl.get_enable());
bps_channel->ctrl.set_enable(true);
bps_channel->ctrl.set_voltage(3.3);
bps_channel->ctrl.set_current(5.0);
spdlog::info(" Voltage: {}", bps_channel->voltmeter.get_measure());
spdlog::info(" Current: {}", bps_channel->ampermeter.get_measure());
spdlog::info(" Enabled: {}", bps_channel->ctrl.get_enable());
}

spdlog::info("\n\nOK\n\n");

while (1)
;

return 0;
}
3 changes: 1 addition & 2 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ target_sources(${LIBRARY_NAME}

pza/devices/bps.cxx

pza/interfaces/ampermeter.cxx
pza/interfaces/voltmeter.cxx
pza/interfaces/meter.cxx
pza/interfaces/bps_chan_ctrl.cxx
)

Expand Down
69 changes: 34 additions & 35 deletions source/pza/core/attribute.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -17,43 +17,39 @@ void attribute::on_message(const mqtt::const_message_ptr &message)

for (auto it = json.begin(); it != json.end(); ++it) {
spdlog::trace("Attribute {:s} received data for field {:s} with value {:s}", _name, it.key(), it.value().dump());

auto data = it.value();
auto elem = _fields.find(it.key());
if (elem != _fields.end())
{
if (is_type_compatible(it.value().type(), elem->second->get_json_type()) == true)
{
spdlog::trace("Setting attribute {:s} field {:s} to value {:s}", _name, it.key(), it.value().dump());
if (elem->second->get_type() == typeid(double))
{
field<double> *f = static_cast<field<double>*>(elem->second);
f->_set_value(it.value());
}
else if (elem->second->get_type() == typeid(int))
{
field<int> *f = static_cast<field<int>*>(elem->second);
f->_set_value(it.value());
}
else if (elem->second->get_type() == typeid(bool))
{
field<bool> *f = static_cast<field<bool>*>(elem->second);
f->_set_value(it.value());
}
else if (elem->second->get_type() == typeid(std::string))
{
field<std::string> *f = static_cast<field<std::string>*>(elem->second);
f->_set_value(it.value());
}
_waiting_for_response = false;
_cv.notify_all();
}
else {
spdlog::warn("Type mismatch for attribute {:s}, field {:s}.. ", _name, it.key());
}

if (elem == _fields.end()) {
continue;
}

auto &f = elem->second;
const auto &type = f.type();

if (type == typeid(field<double>)) {
_assign_value<double>(f, data);
}
else if (type == typeid(field<int>)) {
_assign_value<int>(f, data);
}
else if (type == typeid(field<bool>)) {
_assign_value<bool>(f, data);
}
else if (type == typeid(field<std::string>)) {
_assign_value<std::string>(f, data);
}
else {
spdlog::warn("Type mismatch for attribute {:s}, field {:s}.. ", _name, it.key());
return ;
}
_waiting_for_response = false;
_cv.notify_all();
}
}

bool attribute::is_type_compatible(const nlohmann::json::value_t &value1, const nlohmann::json::value_t &value2)
bool attribute::type_is_compatible(const nlohmann::json::value_t &value1, const nlohmann::json::value_t &value2)
{
constexpr auto INTEGER = nlohmann::json::value_t::number_integer;
constexpr auto UNSIGNED = nlohmann::json::value_t::number_unsigned;
Expand All @@ -67,9 +63,10 @@ bool attribute::is_type_compatible(const nlohmann::json::value_t &value1, const
return (value1 == value2) || (isNumber(value1) && isNumber(value2));
}

void attribute::data_from_field(const nlohmann::json &data)
int attribute::data_from_field(const nlohmann::json &data)
{
nlohmann::json json;
int ret = -1;

json[_name] = data;

Expand All @@ -82,10 +79,12 @@ void attribute::data_from_field(const nlohmann::json &data)

for (int i = 0; i < SET_TIMEOUT_RETRIES; i++)
{
if (_cv.wait_for(lock, std::chrono::seconds(SET_TIMEOUT), [&]()
{ return !_waiting_for_response; }) == true)
if (_cv.wait_for(lock, std::chrono::seconds(SET_TIMEOUT), [&]() {return !_waiting_for_response; }) == true) {
ret = 0;
break;
}
else
spdlog::warn("Timeout while waiting for response from attribute {:s}, retrying...", _name);
}
return ret;
}
37 changes: 25 additions & 12 deletions source/pza/core/attribute.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,45 @@ namespace pza
template<typename T>
void add_field(const std::string &name, bool readonly)
{
auto elem = new field<T>(name, readonly);
_fields[name] = elem;
elem->_callback = std::bind(&attribute::data_from_field, this, std::placeholders::_1);
field<T> field(name, readonly);

field._callback = std::bind(&attribute::data_from_field, this, std::placeholders::_1);
_fields[name] = field;
}

void on_message(const mqtt::const_message_ptr &message);

static bool is_type_compatible(const nlohmann::json::value_t &value1, const nlohmann::json::value_t &value2);
std::map<std::string, _field_base*> _fields;
static bool type_is_compatible(const nlohmann::json::value_t &value1, const nlohmann::json::value_t &value2);

template<typename T>
field<T>* get_field(const std::string& name) {
auto it = _fields.find(name);
if (it != _fields.end()) {
return dynamic_cast<field<T>*>(it->second);
}
return nullptr;
field<T> &get_field(const std::string &name)
{
return std::any_cast<field<T>&>(_fields[name]);
}

private:

static constexpr int SET_TIMEOUT = 1; // in seconds
static constexpr int SET_TIMEOUT_RETRIES = 3;

void data_from_field(const nlohmann::json &data);
int data_from_field(const nlohmann::json &data);

template<typename T>
void _assign_value(std::any &elem, const nlohmann::json &data)
{
try {
auto &f = std::any_cast<field<T>&>(elem);
if (type_is_compatible(data.type(), f.get_json_type()) == true)
f._set_value(data.get<T>());
else
spdlog::error("Type mismatch for attribute {:s}, field {:s}.. ", _name, f.name());
}
catch (const std::bad_any_cast &e) {
spdlog::error("Bad any cast for attribute {:s} : {}", _name, e.what());
}
}

std::map<std::string, std::any> _fields;
std::string _name;
std::condition_variable _cv;
std::mutex _mtx;
Expand Down
2 changes: 1 addition & 1 deletion source/pza/core/device.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace pza
const std::string &get_model() { return _model; }
const std::string &get_manufacturer() { return _manufacturer; }
client *get_client() { return _cli; }
virtual const std::string get_family() = 0;
virtual const std::string &get_family() = 0;

void reset();
enum state get_state() { return _state; }
Expand Down
55 changes: 35 additions & 20 deletions source/pza/core/field.hxx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
#pragma once

#include <list>

#include <nlohmann/json.hpp>

#include <spdlog/spdlog.h>

namespace pza
{
class _field_base
{
public:
virtual const std::type_info &get_type() const = 0;
virtual const nlohmann::json::value_t &get_json_type() const = 0;
};

template <typename _type>
class field : public _field_base
class field
{
public:
friend class attribute;
Expand All @@ -26,28 +21,50 @@ namespace pza
_setJsonType();
}

const std::string &name() const
{
return _name;
}

const _type &get(void) const
{
return _value;
}

void set(const _type &value)
int set(const _type &value)
{
nlohmann::json data;

if (value == _value)
return 0;

if (!_callback) {
spdlog::error("No callback set for field.. Make sure the interface is bound to a client.");
return ;
return -1;
}
data[this->_name] = value;
_callback(data);
return _callback(data);
}

bool is_readonly(void) const
{
return _readonly;
}

using get_callback_type = std::function<void(_type)>;

void add_get_callback(const get_callback_type& callback)
{
_get_callbacks.push_back(std::make_shared<get_callback_type>(callback));
}

void remove_get_callback(const get_callback_type& callback)
{
_get_callbacks.remove_if([&](const std::shared_ptr<get_callback_type>& ptr) {
return callback.target_type() == ptr->target_type();
});
}

private:
void _setJsonType()
{
Expand Down Expand Up @@ -77,31 +94,29 @@ namespace pza
}
}

const nlohmann::json::value_t &get_json_type() const override
const nlohmann::json::value_t &get_json_type() const
{
return _json_type;
}

const std::type_info &get_type() const override
const std::type_info &get_type() const
{
return typeid(_type);
}

void _set_value(const _type &value)
{
_value = value;
//for (auto &cb : _get_callbacks)
//{
// cb();
//}
for (auto const &cb : _get_callbacks) {
(*cb)(value);
}
}

_type _value = _type();
std::string _name;
nlohmann::json::value_t _json_type;

std::list<std::shared_ptr<get_callback_type>> _get_callbacks;
bool _readonly;

std::function<void(const nlohmann::json &data)> _callback;
std::function<int(const nlohmann::json &data)> _callback;
};
};
Loading

0 comments on commit 98a16da

Please sign in to comment.