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

WIP: Support Modbus Meters #272

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ede6873
Reading: Add convinience constructor with value and Id
flyingflo Aug 1, 2016
82311bc
Make OptionList methods static, it makes no sense to have an instance
flyingflo Aug 1, 2016
7cd4d06
Add support for Modbus Smart Meters
flyingflo May 1, 2016
1801abd
ModbusMeter: Copy device config to string
flyingflo Jul 27, 2016
eabf340
MeterModbus: Introduce register maps
flyingflo Aug 1, 2016
85e9afc
Add convenient functions for option lookup
flyingflo Aug 5, 2016
0492b2c
Disable Mock until we have the tests for it
flyingflo Aug 5, 2016
cee7339
gitignore eclipse IDE files
flyingflo Aug 5, 2016
bbfe9a9
Add example modbus meter config
flyingflo Aug 5, 2016
9c1131e
MeterModbus: Universalize register maps
flyingflo Aug 5, 2016
514717b
MeterModbus: config lookup for register maps
flyingflo Aug 5, 2016
31c567e
MeterModbus: Try to recover from read errors with re-connecting
flyingflo Aug 5, 2016
5f73c4b
MeterModbus: Add errno to what()
flyingflo Aug 6, 2016
f8c2366
MeterModbus: Add Import Energy register to IME meter
flyingflo Aug 12, 2016
4b25cb0
MeterModbus: WIP add multi device support
flyingflo Aug 16, 2016
d8a3c50
MeterModbus: Fix ModbusException what() string livetime
flyingflo Aug 18, 2016
825e8ed
MeterModbus: Support multiple slaves per Meter
flyingflo Sep 2, 2017
23aa4f2
ModbusMeter: Bus error handling
flyingflo Aug 18, 2016
7ba20dc
ModbusMeter: config: development test
flyingflo Aug 21, 2016
6cd280f
Meter interval: Account for the time spent with reading
flyingflo Aug 21, 2016
a52d7f1
MeterModbus: Reconnect if no reading on this connection succeeds
flyingflo Aug 22, 2016
edb2a5a
MeterModbus: Add a device-specific bus silence time for IME meters
flyingflo Aug 22, 2016
7e074aa
MeterModbus: Read multi-slave config array
flyingflo Aug 22, 2016
9f54296
MeterModbus: support for re-open a connection on recovery
flyingflo Sep 19, 2016
3c41efe
MeterModbus: Reconnect immediatly to flush state
flyingflo Aug 28, 2017
7582d0e
MeterModbus: bus silence for IME meters after read
flyingflo Aug 28, 2017
1759b5b
MeterModbus: Increase inter-frame wait time to 200ms for IME meter
flyingflo Sep 19, 2016
5649f91
MeterModbus: multi device config
flyingflo Aug 31, 2017
9895172
Revert "Disable Mock until we have the tests for it"
flyingflo Sep 3, 2017
2ff882c
ut_meter: basic integration of MeterModbus
flyingflo Sep 3, 2017
63df238
mock_metermap: remove unused microhttpd library
flyingflo Sep 3, 2017
3e5edc7
MeterModbus: Move ModbusReading to Reading.cpp/.h
flyingflo Sep 3, 2017
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ stamp-h1

# Debian packaging
/debian/config.log

.cproject
.project
.settings/
73 changes: 72 additions & 1 deletion etc/vzlogger.conf
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,78 @@
"enabled": false,
"skip": true,
"protocol": "w1therm"
}
},
{
"enabled": false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indents?

"protocol": "modbus",
"type": "rtu",
"device": "/dev/ttyUSB1",
"baudrate": 9600,
"devices": [ {
"id": 2,
"regmap": "imemeter"
},
{
"id": 1,
"regmap": "imemeter"
}],
"interval": 1,
"channels": [{
"uuid": "037bfe00-0fd1-11e6-a37c-a3524b8cd319",
"identifier": "2:CurrentPowerW",
"middleware": "http://localhost/middleware.php",
},
{
"uuid": "50fd47d0-53df-11e6-9e8d-6312e0309d6c",
"identifier": "2:TotalExpWh",
"middleware": "http://localhost/middleware.php",
"interval_factor": 300,
},
{
"uuid": "3e428540-60c4-11e6-b2f8-d91fd531def7",
"identifier": "1:CurrentPowerW",
"middleware": "http://localhost/middleware.php",
},
{
"uuid": "7073dcf0-60c4-11e6-afbb-c5206eac7c36",
"identifier": "1:TotalImpWh",
"middleware": "http://localhost/middleware.php",
"interval_factor": 30,
}]
},
// example Modbus Meter
{
"enabled": true,
"interval": 1,
"protocol": "modbus",
"type": "tcp",
"host": "192.168.1.86",
"devices": [{
"id": 255,
"regmap": "ohmpilot",
}],
"channels": [{
"api": "null",
"uuid": "5cb9ace0-523f-11e6-a133-2f12ad3ac451",
"identifier": "255:T",
"middleware": "http://localhost/middleware.php",
"interval_factor": 300
},
{
"api": "null",
"uuid": "6b9ec800-5241-11e6-bb2f-dd48308e74d8",
"identifier": "255:P",
"middleware": "http://localhost/middleware.php",
},
{
"api": "null",
"uuid": "3a00f4b0-53df-11e6-b09f-d350e12493ff",
"identifier": "255:E",
"middleware": "http://localhost/middleware.php",
"interval_factor": 300
}]

}
]
}

46 changes: 36 additions & 10 deletions include/Options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <iostream>
#include <list>
#include <json-c/json.h>
#include <common.h>

class Option {

Expand Down Expand Up @@ -87,20 +88,45 @@ class OptionList { //: public List<Option> {
typedef std::list<Option>::iterator iterator;
typedef std::list<Option>::const_iterator const_iterator;

const Option& lookup(std::list<Option> const &options, const std::string &key) const;
const char *lookup_string(std::list<Option> const &options, const char *key) const;
const char *lookup_string_tolower(std::list<Option> const &options, const char *key) const;
int lookup_int(std::list<Option> const &options, const char *key) const;
bool lookup_bool(std::list<Option> const &options, const char *key) const;
double lookup_double(std::list<Option> const &options, const char *key) const;
struct json_object *lookup_json_array(std::list<Option> const &options, const char *key) const;
struct json_object *lookup_json_object(std::list<Option> const &options, const char *key) const;
void dump(std::list<Option> const &options);
static const Option& lookup(std::list<Option> const &options, const std::string &key);
static const char *lookup_string(std::list<Option> const &options, const char *key);
static const char *lookup_string_tolower(std::list<Option> const &options, const char *key);
static int lookup_int(std::list<Option> const &options, const char *key);
static bool lookup_bool(std::list<Option> const &options, const char *key);
static double lookup_double(std::list<Option> const &options, const char *key);
static struct json_object *lookup_json_array(std::list<Option> const &options, const char *key);
static struct json_object *lookup_json_object(std::list<Option> const &options, const char *key);
static void dump(std::list<Option> const &options);

void parse();
static void parse();

protected:

};


template <typename T, T (*L)(const std::list<Option> &, const char *)>
T lookup_mandatory(const std::list<Option> &olist, const std::string &o, const std::string &errorcontext) {
T v;
try {
v = L(olist, o.c_str());
} catch (vz::VZException &e) {
print(log_error, "Missing mandatory option: %s", errorcontext.c_str(), o.c_str());
throw vz::OptionNotFoundException(e.reason());
}
return v;
}

template <typename T, T (*L)(const std::list<Option> &, const char *)>
T lookup_optional(const std::list<Option> &olist, const std::string &o, const T &def) {
T v;
try {
v = L(olist, o.c_str());
} catch (vz::VZException &e) {
return def;
}
return v;
}


#endif /* _OPTIONS_H_ */
20 changes: 20 additions & 0 deletions include/Reading.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,33 @@ class NilIdentifier : public ReadingIdentifier {
private:
};

class ModbusReadingIdentifier : public ReadingIdentifier {
unsigned _slaveid;
std::string _name;
void parse(const std::string& s);
public:
ModbusReadingIdentifier()
:_slaveid(0) {}
ModbusReadingIdentifier(unsigned slave, const std::string& name)
: _slaveid(slave), _name(name) {}
ModbusReadingIdentifier(const std::string& conf);
virtual ~ModbusReadingIdentifier(){};

virtual size_t unparse(char *buffer, size_t n);
virtual bool operator==( ReadingIdentifier const &cmp) const;

virtual const std::string toString();

};

class Reading {

public:
typedef vz::shared_ptr<Reading> Ptr;
Reading();
Reading(ReadingIdentifier::Ptr pIndentifier);
Reading(double pValue, struct timeval pTime, ReadingIdentifier::Ptr pIndentifier);
Reading(double pValue, ReadingIdentifier *pIndentifier);
Reading(const Reading &orig);

bool deleted() const { return _deleted; }
Expand Down
1 change: 1 addition & 0 deletions include/meter_protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ typedef enum meter_procotol {
meter_protocol_ocr,
meter_protocol_w1therm,
meter_protocol_oms,
meter_protocol_modbus,
} meter_protocol_t;
#endif /* _meter_protocol_hpp_ */
122 changes: 122 additions & 0 deletions include/protocols/MeterModbus.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* @file MeterModbus.h
* @brief
* Created on: Apr 25, 2016
* @author Florian Achleitner <[email protected]>
*/
#ifndef METERMODBUS_H_
#define METERMODBUS_H_

#include "protocols/Protocol.hpp"
#include <modbus/modbus.h>
#include <errno.h>
#include <map>

class ModbusException : public std::runtime_error {
int _errno;
std::string _what;
public:
explicit ModbusException(const std::string& arg);
virtual const char *what() const noexcept override {
return _what.c_str();
}

};

class ModbusConnection
{
protected:
modbus_t *_ctx;
bool _connected;
public:
typedef vz::shared_ptr<ModbusConnection> Ptr;
ModbusConnection() :
_ctx(NULL), _connected(false) {}
virtual ~ModbusConnection();
modbus_t *getctx() {
return _ctx;
}

virtual void open() = 0;
virtual void connect();
virtual void close();
virtual void read_registers(int addr, int nb, uint16_t *dest, unsigned slave);
virtual void debug(bool enable);
private:
ModbusConnection(const ModbusConnection&) = delete;
ModbusConnection& operator= (const ModbusConnection&) = delete;
};

class ModbusRTUConnection : public ModbusConnection
{
std::string _device;
int _baud;
public:
ModbusRTUConnection(const std::string &device, int baud)
: _device(device), _baud(baud) {}
virtual void open();
};

class ModbusTCPConnection : public ModbusConnection
{
std::string _ip;
int _port;
public:
ModbusTCPConnection(const std::string &ip, int port = 502)
: _ip(ip), _port(port) {}
virtual void open();
};

class RegisterMap
{
public:
typedef vz::shared_ptr<RegisterMap> Ptr;
virtual ~RegisterMap() {}
virtual void read(std::vector<Reading>& rds, ModbusConnection::Ptr conn, unsigned id) = 0;
static Ptr findMap(const std::string &name) {
return maps.at(name)();
}
private:
template <class T> static Ptr createMap() {
return Ptr(new T());
}

static std::map<std::string, Ptr (*)()> maps;
};
/**
*
*/
class MeterModbus: public vz::protocol::Protocol
{
public:
typedef unsigned slaveid_t;
private:
ModbusConnection::Ptr _mbconn;
bool _libmodbus_debug;
typedef std::map<slaveid_t, RegisterMap::Ptr> SlaveRegMaps;
SlaveRegMaps _devices;

void create_rtu(const std::list<Option> &options);
void create_tcp(const std::list<Option> &options);
public:
MeterModbus(const std::list<Option> &options);
virtual ~MeterModbus();
virtual int open();
virtual int close();
virtual ssize_t read(std::vector<Reading> &rds, size_t n);
};


class OpRegisterMap: public RegisterMap {
public:
virtual ~OpRegisterMap() {}
virtual void read(std::vector<Reading>& rds, ModbusConnection::Ptr conn, unsigned id);
};

class IMEmeterRegisterMap: public RegisterMap {
public:
virtual ~IMEmeterRegisterMap() {}
virtual void read(std::vector<Reading>& rds, ModbusConnection::Ptr conn, unsigned id);
};

#endif /* METERMODBUS_H_ */
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ if( MBUS_FOUND )
target_link_libraries(vzlogger ${MBUS_LIBRARY})
endif( MBUS_FOUND )
target_link_libraries(vzlogger ${OCR_LIBRARIES})
target_link_libraries(vzlogger modbus)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different from mbus_library?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, modbus and mbus have little in common, except the m and the bus ;)


if(LOCAL_SUPPORT)
target_link_libraries(vzlogger ${MICROHTTPD_LIBRARY})
Expand All @@ -72,6 +73,7 @@ if( TARGET )
endif( TARGET )
target_link_libraries(vzlogger ${CURL_STATIC_LIBRARIES} ${CURL_LIBRARIES} ${GNUTLS_LIBRARIES} ${OPENSSL_LIBRARIES} )


# add programs to the install target
INSTALL(PROGRAMS
${CMAKE_CURRENT_BINARY_DIR}/vzlogger
Expand Down
6 changes: 6 additions & 0 deletions src/Meter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#ifdef OMS_SUPPORT
#include "protocols/MeterOMS.hpp"
#endif
#include "protocols/MeterModbus.hpp"
//#include <protocols/.h>

#define METER_DETAIL(NAME, CLASSNAME, DESC, MAX_RDS, PERIODIC) { \
Expand All @@ -72,6 +73,7 @@ static const meter_details_t protocols[] = {
#ifdef OMS_SUPPORT
METER_DETAIL(oms, OMS, "OMS (M-BUS) protocol based devices", 100, false), // todo what is the max. amount of reading according to spec?
#endif
METER_DETAIL(modbus, Modbus, "Modbus meter", 64, true),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surround with ifdef fpor modbus support throughout the vode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It belongs to this todo to make the whole modbus stuff optional

  • Add CMake module for libmodbus and make this feature optional

Still open ..

//{} /* stop condition for iterator */
METER_DETAIL(none, NULL,NULL, 0,false),
};
Expand Down Expand Up @@ -193,6 +195,10 @@ Meter::Meter(std::list<Option> pOptions) :
_identifier = ReadingIdentifier::Ptr(new ObisIdentifier());
break;
#endif
case meter_protocol_modbus:
_protocol = vz::protocol::Protocol::Ptr(new MeterModbus(pOptions));
_identifier = ReadingIdentifier::Ptr(new ModbusReadingIdentifier());
break;
default:
break;
}
Expand Down
Loading