Updating a File (KeyValueEEPROM) to ArduinoJson 6 - arduino

I am an engineer not a programmer, but due to circumstance I am fix the programming for a project I am working on.
This is where I am currently stuck. The KeyValueEEPROM.h is used to store the collected data by the main program, and then read from to upload to a SQL database.
Somewhere in this process I am losing the data collected by the sensor. I believe I isolated the problem to the referenced file.
the first version was made using ArduinoJson 5 (I am not the original creator). This is incompatible with the current program, as ArduinoJson 5 has been upgraded to the 6th version.
The other one is edited by me to use ArduinoJson 6.
I had to change the file to get the codes to compile (which it now does), but I am not sure if I was able to preserve the functions.
I would like someone to point out any mistakes I made during the 'update'.
ArduinoJson5 Version:
#ifndef KeyValueEEPROM_h
#define KeyValueEEPROM_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#ifndef KeyValueEEPROM_SIZE
#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega1280__) ||
defined(__AVR_ATmega2560__)
#define KeyValueEEPROM_SIZE 4096
#endif
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__)
#define KeyValueEEPROM_SIZE 1024
#endif
#if defined(__AVR_ATmega168__)
#define KeyValueEEPROM_SIZE 512
#endif
#endif
class KeyValueEEPROMClass {
public:
// Public methods
void begin() {
// If this method was already called, return.
if (LibraryStartet) return;
// Read JSON data from the EEPROM.
String json = readEEPROM();
// Parse JSON data.
root = &jsonBuffer.parseObject(json);
// If JSON couldn't be parsed,
if (!root->success()) {
// then create an new key-value store.
clear();
}
};
void clear() {
// Clear buffer.
jsonBuffer.clear();
// Create JSON root object.
root = &jsonBuffer.createObject();
};
void remove(String key) {
// Remove key.
root->remove(key);
};
void apply() {
// Clear the EEPROM.
clearEEPROM();
// Extract JSON data from the root object.
String json;
root->printTo(json);
// Write JSON data to the EEPROM.
writeEEPROM(json);
};
bool exists(String key) {
return root->containsKey(key);
};
template <typename T>
T get(String key) {
T value = (*root)[key].as<T>();
return value;
};
template <typename T>
void set(String key, T value) {
(*root)[key] = value;
};
private:
// Private methods
void clearEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write all bytes to zero.
for (int i = 0; i < KeyValueEEPROM_SIZE; i++) EEPROM.write(i, '\0');
// End EEPROM library.
EEPROM.end();
};
String readEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Read JSON data until null-termination.
String string = ""; unsigned int i; unsigned char byte;
for (i = 0; i < KeyValueEEPROM_SIZE; i++) {
byte = EEPROM.read(i);
if (byte == '\0') {
break;
} else {
string += char(byte);
}
}
// End EEPROM library.
EEPROM.end();
// Return JSON data.
return string;
};
void writeEEPROM(String json) {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write JSON data.
unsigned int i;
for (i = 0; i < json.length(); i++) {
EEPROM.write(i, json.charAt(i));
}
// If the length of the JSON data is smaller than the EEPROM size,
if (json.length() < KeyValueEEPROM_SIZE)
// than write at the last position a null-termination.
EEPROM.write(json.length(), '\0');
// End EEPROM library.
EEPROM.end();
}
// Properties
StaticJsonBuffer<KeyValueEEPROM_SIZE> jsonBuffer;
JsonObject *root;
bool LibraryStartet = false;
};
static KeyValueEEPROMClass KeyValueEEPROM;
extern KeyValueEEPROMClass KeyValueEEPROM;
#endif
ArduinoJson6 Version
#ifndef KeyValueEEPROM_h
#define KeyValueEEPROM_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#ifndef KeyValueEEPROM_SIZE
#if defined(ESP8266) || defined(ESP32) || defined(__AVR_ATmega1280__) ||
defined(__AVR_ATmega2560__)
#define KeyValueEEPROM_SIZE 4096
#endif
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__)
#define KeyValueEEPROM_SIZE 1024
#endif
#if defined(__AVR_ATmega168__)
#define KeyValueEEPROM_SIZE 512
#endif
#endif
class KeyValueEEPROMClass {
public:
// Public methods
void begin() {
// If this method was already called, return.
if (LibraryStartet) return;
// Read JSON data from the EEPROM.
String json = readEEPROM();
// Parse JSON data.
DeserializationError error = deserializeJson(doc,json);
// If JSON couldn't be parsed,
if (error) {
// then create an new key-value store.
clear();
}
};
void clear() {
// Clear buffer.
doc.clear();
// Create JSON root object.
// doc = &doc.createObject();
};
void remove(String key) {
// Remove key.
doc.remove(key);
};
void apply() {
// Clear the EEPROM.
clearEEPROM();
// Extract JSON data from the root object.
String json;
//doc.
serializeJson(doc, json);
// Write JSON data to the EEPROM.
writeEEPROM(json);
};
bool exists(String key) {
return doc.containsKey(key);
};
template <typename T>
T get(String key) {
T value = doc[key].as<T>();
return value;
};
template <typename T>
void set(String key, T value) {
doc[key] = value;
};
private:
// Private methods
void clearEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write all bytes to zero.
for (int i = 0; i < KeyValueEEPROM_SIZE; i++) EEPROM.write(i, '\0');
// End EEPROM library.
EEPROM.end();
};
String readEEPROM() {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Read JSON data until null-termination.
String string = ""; unsigned int i; unsigned char byte;
for (i = 0; i < KeyValueEEPROM_SIZE; i++) {
byte = EEPROM.read(i);
if (byte == '\0') {
break;
} else {
string += char(byte);
}
}
// End EEPROM library.
EEPROM.end();
// Return JSON data.
return string;
};
void writeEEPROM(String json) {
#if defined(ESP8266) || defined(ESP32)
// Begin EEPROM library.
EEPROM.begin(KeyValueEEPROM_SIZE);
#endif
// Write JSON data.
unsigned int i;
for (i = 0; i < json.length(); i++) {
EEPROM.write(i, json.charAt(i));
}
// If the length of the JSON data is smaller than the EEPROM size,
if (json.length() < KeyValueEEPROM_SIZE)
// than write at the last position a null-termination.
EEPROM.write(json.length(), '\0');
// End EEPROM library.
EEPROM.end();
}
// Properties
StaticJsonDocument<KeyValueEEPROM_SIZE> doc;
//JsonObject *doc;
JsonObject object = doc.to<JsonObject>();
bool LibraryStartet = false;
};
static KeyValueEEPROMClass KeyValueEEPROM;
extern KeyValueEEPROMClass KeyValueEEPROM;
#endif
This is also my first post here, so if I have formatting issues or lack clarity I apologize.
Thanks,

Related

Subclassing QIODevice: Wrapper for QUdpSocket

I am trying to implement my own wrapper over QUdpSocket because of it is uncomfortable to use. I can use it, but anyway I need to implement some intermediate buffer for access to QDataStream operations. In additional:
I sublass QIODevice,
header (bit simplified):
class BerUdp : public QIODevice
{
Q_OBJECT
void startup();
public:
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
qint64 bytesAvailable() const;
protected: // Reimplement:
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private:
QUdpSocket* dev_write; // udp socket to write
QUdpSocket* dev_read; // udp socket to read
QBuffer m_buffer; // buffer to store received datagrams
};
.cpp file:
void BerUdp::startup()
{
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
bool ok = connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Read new received data to my internal buffer
void BerUdp::onReceive()
{
bool av = dev_read->hasPendingDatagrams();
if (av)
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
// write new data to the end
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
}
From the Qt documuntation on QIODevice::readData()
..When reimplementing this function it is important that this function
reads all the required data before returning. This is required in
order for QDataStream to be able to operate on the class. QDataStream
assumes all the requested information was read and therefore does not
retry reading if there was a problem...:
// Main read data function. There are only 4 bytes required, but maxSize == 0x4000 here:
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already read:
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n); // m_buffer.size() == 0 after this
m_buffer.seek(0);
return n;
}
The problem is that after the first read I have empty buffer, and therefore my bytesAvailable() method returns 0:
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size); // == 0 after 1st read
}
So I can not find out how many bytes are available, when use the class, for example:
BerUdp udp;
QDataStream stream;
stream.setDevice(&udp);
...
QIODevice* dev = stream.device(); // 19 bytes available here
if (dev->bytesAvailable() > 0) // == true
{
quint32 val;
stream >> val;
}
if (dev->bytesAvailable() > 0) // == false
{
//...
}
How can own wrapper of QUdpSocket be written correctly?
The idea of using intermediate buffer worked well, until I decided to move the logic to the separate QIODevice-derived class.
In the process of debugging with the Qt sources, it was found out that I should set the isSequential() property to true. Now my class works correct.
bool BerUdp::isSequential() const
{
return true;
}
Full class:
BerUdp.h
#pragma once
#include <QIODevice>
#include <QBuffer>
class QUdpSocket;
class QHostAddress;
class BerUdp : public QIODevice
{
Q_OBJECT
public:
BerUdp(QObject *parent = 0);
void startup();
void setHostToWrite(const QHostAddress &address, quint16 port);
void setPortToRead(quint16 port);
bool flush();
qint64 bytesAvailable() const;
bool waitForReadyRead(int msecs);
bool isSequential() const;
protected: // Main necessary reimplement
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *data, qint64 maxSize);
private slots:
void onReceive();
private:
void read_udp_datagram();
void write_new_data_to_buffer(QByteArray dtg);
private:
QUdpSocket* dev_write; // One udp socket to write
QUdpSocket* dev_read; // Another udp socket to read
// intermediate buffer to store received datagrams
// and to provide access to read- and QDataStream- operations
QBuffer m_buffer;
};
BerUdp.cpp
#include "BerUdp.h"
#include <QUdpSocket>
BerUdp::BerUdp(QObject *parent)
: QIODevice(parent)
{
startup();
}
// Initialization
void BerUdp::startup()
{
dev_write = new QUdpSocket(this);
dev_read = new QUdpSocket(this);
m_buffer.open(QIODevice::ReadWrite);
open(QIODevice::ReadWrite);
}
// Set a virtual connection to "host"
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
dev_write->connectToHost(address, port);
dev_write->waitForConnected();
}
// Bind a port for receive datagrams
void BerUdp::setPortToRead(quint16 port)
{
dev_read->bind(port);
dev_read->open(QIODevice::ReadOnly);
connect(dev_read, &QIODevice::readyRead,
this, &QIODevice::readyRead);
connect(dev_read, &QIODevice::readyRead,
this, &BerUdp::onReceive);
}
// Flush written data
bool BerUdp::flush()
{
return dev_write->flush();
}
// Returns the number of bytes that are available for reading.
// Subclasses that reimplement this function must call
// the base implementation in order to include the size of the buffer of QIODevice.
qint64 BerUdp::bytesAvailable() const
{
qint64 my_size = m_buffer.size();
qint64 builtin_size = QIODevice::bytesAvailable();
return (my_size + builtin_size);
}
bool BerUdp::waitForReadyRead(int msecs)
{
return dev_read->waitForReadyRead(msecs);
}
// Socket device should give sequential access
bool BerUdp::isSequential() const
{
return true;
}
// This function is called by QIODevice.
// It is main function for provide access to read data from QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
int n = m_buffer.read(data, maxSize);
// clear the data which has already been read
QByteArray& ba = m_buffer.buffer();
ba.remove(0, n);
m_buffer.seek(0);
return n;
}
// This function is called by QIODevice.
// It is main function for provide access to write data to QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::writeData(const char *data, qint64 maxSize)
{
return dev_write->write(data, maxSize);
}
// Read new available datagram
void BerUdp::read_udp_datagram()
{
int dSize = dev_read->pendingDatagramSize();
QByteArray dtg(dSize, 0);
dev_read->readDatagram(dtg.data(), dtg.size());
write_new_data_to_buffer(dtg);
}
// Write received data to the end of internal intermediate buffer
void BerUdp::write_new_data_to_buffer(QByteArray dtg)
{
int buf_read_pos = m_buffer.pos();
m_buffer.seek(m_buffer.size());
m_buffer.write(dtg);
m_buffer.seek(buf_read_pos);
}
// Is called on readyRead signal
void BerUdp::onReceive()
{
bool available = dev_read->hasPendingDatagrams();
if (available)
{
read_udp_datagram();
}
}

How to attach to existing shared memory from Qt?

I have created a shared memory segment with the help of a binary in C and written some data into it. Now I want read that data from Qt. How to attach to existing shared memory from Qt?
QSharedMemory isn't really meant to interoperate with anything else. On Unix, it is implemented via SYSV shared memory, but it passes Qt-specific arguments to ftok:
::ftok(filename.constData(), qHash(filename, proj_id));
You could emulate this behavior in your C code, but I don't think it's necessary.
Instead of opening a shared memory segment, simply map a file to memory, and access it from multiple processes. On Qt, QFile::map does what you need.
The example below shows both techniques: using SYSV shared memory and using memory-mapped files:
// https://github.com/KubaO/stackoverflown/tree/master/questions/sharedmem-interop-39573295
#include <QtCore>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <stdexcept>
#include <string>
First, let's have a shared data structure.
struct Data {
int a = 1;
bool b = true;
char c = 'S';
bool operator==(const Data & o) const { return o.a == a && o.b == b && o.c == c; }
static void compare(const void * a, const void * b) {
auto data1 = reinterpret_cast<const Data*>(a);
auto data2 = reinterpret_cast<const Data*>(b);
Q_ASSERT(*data1 == *data2);
}
};
We definitely want error checking, so let's add some helpers that make that easier:
void check(bool ok, const char * msg, const char * detail) {
if (ok) return;
std::string str{msg};
str.append(": ");
str.append(detail);
throw std::runtime_error{str};
}
void check(int f, const char * msg) { check(f != -1, msg, strerror(errno)); }
void check(void * f, const char * msg) { check(f != MAP_FAILED, msg, strerror(errno)); }
void check(bool rc, const QSharedMemory & shm, const char * msg) { check(rc, msg, shm.errorString().toLocal8Bit()); }
void check(bool rc, const QFile & file, const char * msg) { check(rc, msg, file.errorString().toLocal8Bit()); }
And we need RAII wrappers for C APIs:
struct noncopyable { Q_DISABLE_COPY(noncopyable) noncopyable() {} };
struct ShmId : noncopyable {
int id;
ShmId(int id) : id{id} {}
~ShmId() { if (id != -1) shmctl(id, IPC_RMID, NULL); }
};
struct ShmPtr : noncopyable {
void * ptr;
ShmPtr(void * ptr) : ptr{ptr} {}
~ShmPtr() { if (ptr != (void*)-1) shmdt(ptr); }
};
struct Handle : noncopyable {
int fd;
Handle(int fd) : fd{fd} {}
~Handle() { if (fd != -1) close(fd); }
};
Here's how to interoperates SYSV shared memory sections between C and Qt. Unfortunately, unless you reimplement qHash in C, it's not possible:
void ipc_shm_test() {
QTemporaryFile shmFile;
check(shmFile.open(), shmFile, "shmFile.open");
// SYSV SHM
auto nativeKey = QFile::encodeName(shmFile.fileName());
auto key = ftok(nativeKey.constData(), qHash(nativeKey, 'Q'));
check(key, "ftok");
ShmId id{shmget(key, sizeof(Data), IPC_CREAT | 0600)};
check(id.id, "shmget");
ShmPtr ptr1{shmat(id.id, NULL, 0)};
check(ptr1.ptr, "shmat");
new (ptr1.ptr) Data;
// Qt
QSharedMemory shm;
shm.setNativeKey(shmFile.fileName());
check(shm.attach(QSharedMemory::ReadOnly), shm, "shm.attach");
auto ptr2 = shm.constData();
Data::compare(ptr1.ptr, ptr2);
}
Here's how to interoperate memory-mapped files:
void mmap_test() {
QTemporaryFile shmFile;
check(shmFile.open(), "shmFile.open");
shmFile.write({sizeof(Data), 0});
check(true, shmFile, "shmFile.write");
check(shmFile.flush(), shmFile, "shmFile.flush");
// SYSV MMAP
Handle fd{open(QFile::encodeName(shmFile.fileName()), O_RDWR)};
check(fd.fd, "open");
auto ptr1 = mmap(NULL, sizeof(Data), PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd.fd, 0);
check(ptr1, "mmap");
new (ptr1) Data;
// Qt
auto ptr2 = shmFile.map(0, sizeof(Data));
Data::compare(ptr1, ptr2);
}
And finally, the test harness:
int main() {
try {
ipc_shm_test();
mmap_test();
}
catch (const std::runtime_error & e) {
qWarning() << e.what();
return 1;
}
return 0;
}

How to stop QElapsedTimer?

QElapsedTimer timer;
timer.start();
slowOperation1();
qDebug() << "The slow operation took" << timer.elapsed() << "milliseconds";
http://doc.qt.io/qt-5/qelapsedtimer.html#invalidate
After qDebug() I would want to stop this timer. I can't see a stop function there, nor a single shot property.
What's the way out?
You can't stop QElapsedTimer, because there is no timer. When you call method start(), QElapsedTimer saves the current time.
From qelapsedtimer_generic.cpp
void QElapsedTimer::start() Q_DECL_NOTHROW
{
restart();
}
qint64 QElapsedTimer::restart() Q_DECL_NOTHROW
{
qint64 old = t1;
t1 = QDateTime::currentMSecsSinceEpoch();
t2 = 0;
return t1 - old;
}
When elapsed, it gets current time again, and calculate difference.
qint64 QElapsedTimer::elapsed() const Q_DECL_NOTHROW
{
return QDateTime::currentMSecsSinceEpoch() - t1;
}
P.S. Specific realization is platform dependent: Windows, Unix, Mac
QElapsedTimer will use the platform's monotonic reference clock in all platforms that support it. This has the added benefit that QElapsedTimer is immune to time adjustments, such as the user correcting the time. Also unlike QTime, QElapsedTimer is immune to changes in the timezone settings, such as daylight-saving periods.
https://doc.qt.io/qt-5/qelapsedtimer.html#details
I needed an elapsed timer that wouldn't count the paused time, so here's what I came up with:
ElapsedTimer.hpp:
#pragma once
#include <time.h>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <errno.h>
namespace your_namespace {
class ElapsedTimer {
public:
ElapsedTimer();
~ElapsedTimer();
void Continue();
void Pause();
int64_t elapsed_ms();
private:
struct timespec start_ = {};
int64_t worked_time_ = 0;
/// CLOCK_MONOTONIC_COARSE is faster but less precise
/// CLOCK_MONOTONIC_RAW is slower but more precise
const clockid_t clock_type_ = CLOCK_MONOTONIC_RAW;
};
}
ElapsedTimer.cpp:
#include "ElapsedTimer.hpp"
namespace your_namespace {
ElapsedTimer::ElapsedTimer() {}
ElapsedTimer::~ElapsedTimer() {}
inline int64_t GetDiffMs(const timespec &end, const timespec &start) {
return (end.tv_sec - start.tv_sec) * 1000L + (end.tv_nsec - start.tv_nsec) / 1000000L;
}
void ElapsedTimer::Continue()
{
int status = clock_gettime(clock_type_, &start_);
if (status != 0)
printf("%s", strerror(errno));
}
int64_t ElapsedTimer::elapsed_ms()
{
const bool paused = (start_.tv_sec == 0 && start_.tv_nsec == 0);
if (paused)
return worked_time_;
struct timespec now;
int status = clock_gettime(clock_type_, &now);
if (status != 0)
printf("%s", strerror(errno));
const int64_t extra = GetDiffMs(now, start_);
return worked_time_ + extra;
}
void ElapsedTimer::Pause() {
struct timespec now;
int status = clock_gettime(clock_type_, &now);
if (status != 0)
printf("%s", strerror(errno));
worked_time_ += GetDiffMs(now, start_);
start_ = {};
}
}
To be used as:
my_namespace::ElapsedTimer timer;
timer.Continue(); // starts recording the amount of time
timer.Pause();// stops recording time
///do something else
timer.Continue();// resumes recording time
/// and at any time call this to find out how many
/// ms passed excluding the paused time:
int64_t passed_ms = timer.elapsed_ms();

What is this error about?

ALL,
I'm using Anjuta to do my development.
I created a project for my main application, and then made 2 more: 1 for the static library (libdbinterface.a) and 1 for the dynamic library (libsqlite_lib.so).
Both those libraries contains one exported class each: libdbinterface.a - class Database, libsqlite_lib.so - public SQLiteDatabase : public Database.
Now I'm trying to link libdbinterface.a to libsqlite_lib.so.
So in Anjuta I added following to the "Linker Option" for the target libsqlite_lib.so:
-L/home/igor/dbhandler/Debug/dbinterface -ldbinterface
However, trying to compile I received following error from linker:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /home/igor/dbhandler/Debug/dbinterface/libdbinterface.a(database.o): relocation R_X86_64_32S against `_ZTV8Database' can not be used when making a shared object; recompile with -fPIC
I tried to recompile libsqlite_lib.so with -fPIC explicitely added to "C++ Options" of that project but that didn't solve it - I still receive the same error.
Unfortunately trying to Google on how to link .a and .so is not helpful.
Can someone sched some light on how to fix this error?
TIA.
[EDIT]
libsqlite_lib.so Makefile - https://bpaste.net/show/1495231e58cc
libdbinterface.a Makefile - https://bpaste.net/show/3a71c119d0fc
libdbinterface.a contains 2 files databse.h:
#ifndef DBMANAGER_DATABASE
#define DBMANAGER_DATABASE
class Field
{
public:
Field(const std::string &columnName, const std::string &columnType, const std::string &columnDefaultValue = "", const bool columnIsNull = false, const bool columnPK = false)
{
column_name = columnName;
column_type = columnType;
column_defaultValue = columnDefaultValue;
column_isNull = columnIsNull;
column_pk = columnPK;
}
private:
std::string column_name, column_type, column_defaultValue;
bool column_isNull, column_pk;
};
struct FKField
{
FKField(const std::string &table_name, const std::string &original_field, const std::string &referenced_field)
{
tableName = table_name;
originalField = original_field;
referencedField = referenced_field;
}
std::string tableName, originalField, referencedField;
};
class Table
{
public:
Table(const std::string &tableName, const std::vector<Field> &tableFields, const std::map<int,std::vector<FKField> > &foreignKeys)
{
table_name = tableName;
table_fields = tableFields;
foreign_keys = foreignKeys;
}
const std::string &GetTableName() { return table_name; }
std::map<int,std::vector<FKField> > &GetForeignKeyVector() { return foreign_keys; }
private:
std::string table_name;
std::vector<Field> table_fields;
std::map<int,std::vector<FKField> > foreign_keys;
};
#ifdef WIN32
class __declspec(dllexport) Database
#else
class Database
#endif
{
private:
struct Impl;
Impl *pimpl;
public:
Database();
virtual ~Database();
Impl &GetTableVector();
static void *operator new(std::size_t size);
static void operator delete(void *ptr, std::size_t size);
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
virtual int GetTableListFromDb(std::string &) { return 0; }
};
#endif
and database.cpp:
#ifdef WIN32
#include <windows.h>
#endif
#include <map>
#include <vector>
#include <string>
#include <sqlext.h>
#include "database.h"
struct Database::Impl
{
std::vector<Table> m_tables;
};
Database::Database() : pimpl( new Impl )
{
}
Database::~Database()
{
delete pimpl;
}
void *Database::operator new(std::size_t size)
{
return ::operator new( size );
}
void Database::operator delete(void *ptr, std::size_t size)
{
return ::operator delete( ptr );
}
Database::Impl &Database::GetTableVector()
{
return *pimpl;
}
int Database::Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg)
{
selectedDSN = selectedDSN;
errorMsg = errorMsg;
return 0;
}
libsqlite_lib.so has also 2 files: database_sqlite.h
#ifndef DBMANAGER_SQLITE
#define DBMANAGER_SQLITE
#ifdef WIN32
class __declspec(dllexport) SQLiteDatabase : public Database
#else
class SQLiteDatabase : public Database
#endif
{
public:
SQLiteDatabase();
~SQLiteDatabase();
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
virtual int GetTableListFromDb(std::vector<std::wstring> &errorMsg);
protected:
void GetErrorMessage(int code, std::wstring &errorMsg);
private:
sqlite3 *m_db;
};
#endif
and database_sqlite.cpp with the actual implementation.
[/EDIT]
Well, apparently the solution is to rebuild static library with "-fPIC", not the dynamic one.
Thank you for reading.

Linking pcl library with ROS

in pcd_register.cpp
I have code,
#include <ros/ros.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <terminal_tools/print.h>
#include <terminal_tools/parse.h>
#include <terminal_tools/time.h>
#include <pcl/registration/icp.h>
#include <pcl/registration/icp_nl.h>
#include <pcl/registration/gicp.h>
#include <pcl/registration/transforms.h>
#include <pcl/filters/voxel_grid.h> // TEMP: currently used instead of octomap
#include "pcl/registration/types.h"
using namespace pcl::registration;
int main (int argc, char** argv) {
ros::init (argc, argv, "pcd_register");
ros::Time::init();
terminal_tools::TicToc tictoc;
// Downsampling
bool downsampling_enabled = true;
double downsampling_leaf_size = 0.001;
terminal_tools::parse_argument(argc, argv, "-d", downsampling_enabled);
terminal_tools::parse_argument(argc, argv, "-D", downsampling_leaf_size);
// read point clouds
vector<PointCloud> data;
std::string extension (".pcd");
for (int i = 1; i < argc; i++)
{
string fname = string (argv[i]);
if (fname.size () <= extension.size ())
continue;
transform (fname.begin (), fname.end (), fname.begin (), (int(*)(int))tolower);
if (fname.compare (fname.size () - extension.size (), extension.size (), extension) == 0)
{
PointCloud cloud;
pcl::io::loadPCDFile (argv[i], cloud);
if (cloud.points.size() == 0) continue;
data.push_back(cloud);
terminal_tools::print_highlight("Read point cloud from "); terminal_tools::print_value("%s", argv[i]); terminal_tools::print_info(" containing "); terminal_tools::print_value("%d", cloud.points.size()); terminal_tools::print_info(" points.\n");
}
}
if (data.empty ())
{
ROS_ERROR ("Syntax is: %s <source.pcd> <target.pcd> [*]", argv[0]);
ROS_ERROR ("[*] - multiple files can be added. The registration results of (i, i+1) will be registered against (i+2), etc");
ROS_INFO ("Example: %s `rospack find pcl`/test/bun0.pcd `rospack find pcl`/test/bun4.pcd", argv[0]);
return (-1);
}
terminal_tools::print_highlight(" Loaded "); terminal_tools::print_value("%d ", (int)data.size ()); terminal_tools::print_info("datasets.\n");
pcl::GeneralizedIterativeClosestPoint<pcl::registration::Point, pcl::registration::Point> reg;
PointCloud cloud_input, cloud_output, cloud_model;
char filename[255];
unsigned int number_clouds_processed = 0;
for (unsigned int i = 0; i < data.size(); ++i) {
// some filtering or whatever here ...
if (downsampling_enabled) {
pcl::VoxelGrid<Point> sor;
sor.setInputCloud(boost::make_shared<PointCloud>(data[i]));
sor.setLeafSize(downsampling_leaf_size, downsampling_leaf_size, downsampling_leaf_size);
sor.filter(cloud_input);
}
else
cloud_input = data[i];
// write input files
sprintf(filename, "registration_input_%04d.pcd", number_clouds_processed);
pcl::io::savePCDFileBinary(filename, cloud_input);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (number_clouds_processed == 0) { // no registration needed
cloud_output = cloud_input;
cloud_model = cloud_input;
terminal_tools::print_highlight("Adding first scan to model.\n");
// TODO: simply use first point cloud to update/initialize world mode, get cloud_model for next registration from world model
}
else {
//terminal_tools::print_highlight("Registering scan %04d (%d points) against model (%d points)", number_clouds_processed, cloud_input.points.size(), cloud_model.size());
terminal_tools::print_highlight("Registering scan "); terminal_tools::print_value("%04d ", number_clouds_processed);
terminal_tools::print_info("("); terminal_tools::print_value("%d", cloud_input.points.size()); terminal_tools::print_info(" points) against model ("); terminal_tools::print_value("%d ", cloud_model.size()); terminal_tools::print_info("points)");
tictoc.tic();
reg.setInputCloud(boost::make_shared<const PointCloud>(cloud_input));
reg.setInputTarget(boost::make_shared<const PointCloud>(cloud_model));
reg.setMaximumIterations(2); // only one iteration, gicp implementation has inner iterations in optimization
reg.align(cloud_output); // cloud_output = registered point cloud
terminal_tools::print_info("[done, "); terminal_tools::print_value("%g", tictoc.toc()); terminal_tools::print_info("s]\n");
// setting up new model
//cloud_model_normals += cloud_output; // TEMP
double model_subsampling_leaf_size = downsampling_leaf_size; // TEMP
PointCloud cloud_temp; // TEMP
cloud_model += cloud_output; // TEMP
pcl::VoxelGrid<Point> grid; // TEMP
grid.setInputCloud(boost::make_shared<PointCloud>(cloud_model)); // TEMP
grid.setLeafSize(model_subsampling_leaf_size, model_subsampling_leaf_size, model_subsampling_leaf_size); // TEMP
grid.filter(cloud_temp); // TEMP
cloud_model = cloud_temp; // TEMP
// TODO: use cloud_output to udpate world model, get cloud_model from world model
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// write output files
sprintf(filename, "registration_output_%04d.pcd", number_clouds_processed);
pcl::io::savePCDFileBinary(filename, cloud_output);
++number_clouds_processed;
}
pcl::io::savePCDFileBinary("registration_final_model.pcd", cloud_model);
return 0;
}
but when i am compiling it does not know all the terminal_tools header files. but i have downloaded ROS.
Do I have to specify or something on CMakeLists.txt? this is my cmake
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(pcd_register)
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (pcd_register pcd_register.cpp)
target_link_libraries (pcd_register ${PCL_LIBRARIES})
I have a ROS running a similar node using the PCL library and your CMakeLists.txt looks good regarding PCL. If cmake is not finding the terminal tools headers, it is because you have not specified to add those include directories within cmake. In general, if you know how to compile with g++ myprogram.cpp -I${INCLUDE_DIRS} -L${LIB_DIRS} -l${LIBS}, then you need to add the following lines to your CMakeLists:
include_directories(${INCLUDE_DIRS})
link_directories(${LIB_DIRS})
target_link_libraries(pcd_register ${LIBS}).
In your case, you need to specify the include directories of terminal_tools with an include_directories line.

Resources