Commit 61d217e3 authored by Axel Auweter's avatar Axel Auweter
Browse files

Add unit conversion feature.

parent abdb3715
......@@ -11,6 +11,7 @@ OBJS = src/connection.o \
src/timestamp.o \
src/sensorconfig.o \
src/sensorid.o \
src/unitconv.o \
src/c_api.o
# List of public header files necessary to use this libray
......
/*
* unitconv.h
*
* Created on: Aug 12, 2015
* Author: Axel Auweter
*/
#ifndef UNITCONV_H
#define UNITCONV_H
#include <cstdint>
#include <string>
typedef enum {
/* Undefined */
DCDBUnit_None,
/* Base units */
DCDBUnit_Meter,
DCDBUnit_Second,
DCDBUnit_Ampere,
DCDBUnit_Kelvin,
DCDBUnit_Watt,
DCDBUnit_Volt,
/* 1e-3 */
DCDBUnit_MilliMeter,
DCDBUnit_MilliSecond,
DCDBUnit_MilliAmpere,
DCDBUnit_MilliKelvin,
DCDBUnit_MilliWatt,
DCDBUnit_MilliVolt,
/* 1e-6 */
DCDBUnit_MicroMeter,
DCDBUnit_MicroSecond,
DCDBUnit_MicroAmpere,
DCDBUnit_MicroKelvin,
DCDBUnit_MicroWatt,
DCDBUnit_MicroVolt,
/* Others */
DCDBUnit_Celsius,
DCDBUnit_Fahrenheit
} DCDBUnit;
class UnitConv
{
public:
static DCDBUnit fromString(std::string unit);
static std::string toString(DCDBUnit unit);
static bool convert(int64_t& value, DCDBUnit from, DCDBUnit to);
};
#endif
/*
* unitconv.cpp
*
* Created on: Aug 12, 2015
* Author: Axel Auweter
*/
#include "unitconv.h"
#include <list>
/*
Base units
DCDBUnit_Meter,
DCDBUnit_Second,
DCDBUnit_Ampere,
DCDBUnit_Kelvin,
DCDBUnit_Watt,
DCDBUnit_Volt,
1e-3
DCDBUnit_MilliMeter,
DCDBUnit_MilliSecond,
DCDBUnit_MilliAmpere,
DCDBUnit_MilliKelvin,
DCDBUnit_MilliWatt,
DCDBUnit_MilliVolt,
1e-6
DCDBUnit_MicroMeter,
DCDBUnit_MicroSecond,
DCDBUnit_MicroAmpere,
DCDBUnit_MicroKelvin,
DCDBUnit_MicroWatt,
DCDBUnit_MicroVolt,
Others
DCDBUnit_Celsius,
DCDBUnit_Fahrenheit
*/
typedef struct {
DCDBUnit unit;
std::string str;
DCDBUnit baseUnit;
int64_t baseConvFactor; /* a baseConFactor of -1000 indicates that the value has to be divided by 1000 to convert to base, +1000 means to multiply with 1000 */
double baseConvOffset; /* Conversion always first multiplies/divides, then adds the offset */
} ConversionTableEntry;
/*
* Note: since we are using negative offsets into this table to indicate reverse conversion,
* the first entry (index 0) in the table must be an identity conversion.
*/
ConversionTableEntry conversionTable[] = {
/* 0 */ { DCDBUnit_None, "none", DCDBUnit_None, 1, 0},
/* 1 */ { DCDBUnit_Meter, "m", DCDBUnit_Meter, 1, 0 },
/* 2 */ { DCDBUnit_MilliMeter, "mm", DCDBUnit_Meter, -1000, 0 },
/* 3 */ { DCDBUnit_MicroMeter, "um", DCDBUnit_Meter, -1000000, 0 },
/* 4 */ { DCDBUnit_Second, "s", DCDBUnit_Second, 1, 0 },
/* 5 */ { DCDBUnit_MilliSecond, "ms", DCDBUnit_Second, -1000, 0 },
/* 6 */ { DCDBUnit_MicroSecond, "us", DCDBUnit_Second, -1000000, 0 },
/* 7 */ { DCDBUnit_Ampere, "A", DCDBUnit_Ampere, 1, 0 },
/* 8 */ { DCDBUnit_MilliAmpere, "mA", DCDBUnit_Ampere, -1000, 0 },
/* 9 */ { DCDBUnit_MicroAmpere, "mA", DCDBUnit_Ampere, -1000000, 0 },
/* 10 */ { DCDBUnit_Kelvin, "K", DCDBUnit_Kelvin, 1, 0 },
/* 11 */ { DCDBUnit_MilliKelvin, "mK", DCDBUnit_Kelvin, -1000, 0 },
/* 12 */ { DCDBUnit_MicroKelvin, "uK", DCDBUnit_Kelvin, -1000000, 0 },
/* 13 */ { DCDBUnit_Watt, "W", DCDBUnit_Watt, 1, 0 },
/* 14 */ { DCDBUnit_MilliWatt, "mW", DCDBUnit_Watt, -1000, 0 },
/* 15 */ { DCDBUnit_MicroWatt, "uW", DCDBUnit_Watt, -1000000, 0 },
/* 16 */ { DCDBUnit_Volt, "V", DCDBUnit_Volt, 1, 0 },
/* 17 */ { DCDBUnit_MilliVolt, "mV", DCDBUnit_Volt, -1000, 0 },
/* 18 */ { DCDBUnit_MicroVolt, "uV", DCDBUnit_Volt, -1000000, 0 },
/* 19 */ { DCDBUnit_Celsius, "C", DCDBUnit_MilliKelvin, 1000, 273150 },
/* 20 */ { DCDBUnit_Fahrenheit, "F", DCDBUnit_MilliKelvin, 555, 255116 },
};
#define ConversionTableSize ((int)(sizeof(conversionTable)/sizeof(ConversionTableEntry)))
DCDBUnit UnitConv::fromString(std::string unit) {
/* Walk the table to find the string */
for (int i=0; i<ConversionTableSize; i++) {
if ((unit.length() == conversionTable[i].str.length()) && (conversionTable[i].str.compare(unit) == 0)) {
return conversionTable[i].unit;
}
}
/* No match --> return None */
return DCDBUnit_None;
}
std::string UnitConv::toString(DCDBUnit unit) {
/* Walk the table to find the unit */
for (int i=0; i<ConversionTableSize; i++) {
if (conversionTable[i].unit == unit) {
return conversionTable[i].str;
}
}
return std::string("");
}
static bool dfs(std::list<int>& chain, DCDBUnit in, DCDBUnit out) {
/* If there is nothing to convert, we don't have to search */
if (in == out) {
return true;
}
/* If the search depth exceeds the number of entries in the conversion table, abort */
if (chain.size() > ConversionTableSize) {
return false;
}
for (int i=0; i<ConversionTableSize; i++) {
if ((conversionTable[i].unit == in) && (conversionTable[i].unit != conversionTable[i].baseUnit)) {
/* Make sure this is not just the reverse of the last operation */
if ((chain.size() == 0) || (chain.back() != -i)) {
/* If this conversion leads us to the result, we're done */
chain.push_back(i);
if (conversionTable[i].baseUnit == out) {
return true;
}
/* If this conversion can handle our in unit, search for possible follow-up conversions */
if (dfs(chain, conversionTable[i].baseUnit, out)) {
return true;
}
else {
chain.pop_back();
}
}
}
if (conversionTable[i].baseUnit == in && (conversionTable[i].unit != conversionTable[i].baseUnit)) {
if ((chain.size() == 0) || (chain.back() != i)) {
chain.push_back(-i);
if (conversionTable[i].unit == out) {
return true;
}
if (dfs(chain, conversionTable[i].unit, out)) {
return true;
}
else {
chain.pop_back();
}
}
}
}
return false;
}
bool UnitConv::convert(int64_t& value, DCDBUnit in, DCDBUnit out) {
/* Do a depth-first search to find a suitable conversion path */
std::list<int> convChain;
if (dfs(convChain, in, out)) {
/* Convert */
double factor = 1;
double offset = 0;
for (std::list<int>::iterator it = convChain.begin(); it != convChain.end(); it++) {
double newFact = (*it) < 0 ? -conversionTable[-(*it)].baseConvFactor : conversionTable[*it].baseConvFactor;
double newOff = (*it) < 0 ? -conversionTable[-(*it)].baseConvOffset : conversionTable[*it].baseConvOffset;
double absFactor = factor > 0 ? factor : -factor;
double absNewFact = newFact > 0 ? newFact : -newFact;
/* Do offset calculation */
if (newFact > 0) {
offset = (offset * newFact) + newOff;
}
else {
offset = (offset + newOff) / (-newFact);
}
/* Do factor calculation */
if (factor > 0 && newFact > 0) {
factor = factor * newFact;
}
else if (factor < 0 && newFact < 0) {
factor = -(factor * newFact);
}
else if (absFactor == absNewFact) {
factor = 1;
}
else if (absFactor > absNewFact && factor > 0) {
factor = (absFactor / absNewFact);
}
else if (absFactor < absNewFact && newFact > 0) {
factor = (absNewFact / absFactor);
}
else if (absFactor > absNewFact && factor < 0) {
factor = -(absFactor / absNewFact);
}
else if (absFactor < absNewFact && newFact < 0) {
factor = -(absNewFact / absFactor);
}
else {
return false;
}
}
/* Calculate final result */
if (factor > 0) {
value = (value * factor) + (int64_t)offset;
}
else {
value = (value / (-factor)) + (int64_t)offset;
}
return true;
}
return false;
}
include ../config.mk
PROJECTS = dcdbconfig dcdbquery
PROJECTS = dcdbconfig dcdbquery dcdbunitconv
.PHONY : clean install $(PROJECTS)
......
......@@ -59,6 +59,24 @@ void DCDBQuery::doQuery(const char* hostname, std::list<std::string> sensors, DC
/* Iterate over list of sensors requested by the user */
for (std::list<std::string>::iterator it = sensors.begin(); it != sensors.end(); it++) {
bool unitConvert = false;
std::string targetUnitStr;
DCDBUnit baseUnit = DCDBUnit_None, targetUnit = DCDBUnit_None;
/* Check if the sensor was requested in a different unit */
if (it->find('/') != std::string::npos) {
targetUnitStr = it->substr(it->find('/')+1, it->length());
*it = it->substr(0, it->find('/'));
targetUnit = UnitConv::fromString(targetUnitStr);
DCDBPublicSensor sen;
sensorConfig.getPublicSensorByName(sen, it->c_str());
baseUnit = UnitConv::fromString(sen.unit);
unitConvert = true;
}
/* Lookup the sensor in the published sensors table */
std::string pattern;
switch (sensorConfig.getSensorPattern(pattern, *it)) {
......@@ -97,6 +115,16 @@ void DCDBQuery::doQuery(const char* hostname, std::list<std::string> sensors, DC
for (std::list<SensorDataStoreReading>::iterator rit = readings.begin(); rit != readings.end(); rit++) {
SensorDataStoreReading reading = *rit;
/* Convert the unit if requested */
if (unitConvert) {
if (!UnitConv::convert(reading.value, baseUnit, targetUnit)) {
std::cerr << "Warning, cannot convert units ("
<< UnitConv::toString(baseUnit) << " -> "
<< UnitConv::toString(targetUnit) << ")" << std::endl;
unitConvert = false;
}
}
/* Print the sensor's public name */
std::cout << *it << ",";
......
......@@ -13,6 +13,7 @@
#include "dcdb/sensorid.h"
#include "dcdb/sensordatastore.h"
#include "dcdb/sensorconfig.h"
#include "dcdb/unitconv.h"
#ifndef QUERY_H
#define QUERY_H
......
include ../../config.mk
CXXFLAGS = -O2 -ggdb --std=c++11 -Wall -Wno-unused-local-typedefs -Wno-unknown-warning-option -fmessage-length=0 -I$(DCDBDEPLOYPATH)/include/ -I$(DCDBBASEPATH)/include/ -DBOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
OBJS = dcdbunitconv.o
LIBS = -L$(DCDBDEPLOYPATH)/lib/ -ldcdb -lcassandra -luv -lboost_random -lboost_system -lboost_date_time -lssl -lcrypto
TARGET = dcdbunitconv
.PHONY : clean install
$(TARGET): $(OBJS)
$(CXX) -o $(TARGET) $(OBJS) $(LIBS)
all: $(TARGET)
clean:
rm -f $(TARGET)
rm -f $(OBJS)
install: $(TARGET)
install $(TARGET) $(DCDBDEPLOYPATH)/bin/
/*
* dcdbunitconv.cpp
*
* Created on: Aug 12, 2015
* Author: Axel Auweter
*/
/* C++ standard headers */
#include <iostream>
/* C standard headers */
#include <cstdint>
#include <cinttypes>
/* Custom headers */
#include "dcdb/unitconv.h"
int main(int argc, const char* argv[])
{
/* Check command line */
if (argc < 4) {
std::cout
<< "Usage: " << argv[0] << " <value> <from> <to>" << std::endl
<< " where <value> is a integer value" << std::endl
<< " <from> is the source unit" << std::endl
<< " <to> is the target unit" << std::endl;
return 1;
}
/* Parse command line */
int64_t value;
DCDBUnit from, to;
if (sscanf(argv[1], "%" SCNd64, &value) != 1) {
std::cout << "Cannot interpret " << argv[1] << std::endl;
return 2;
}
from = UnitConv::fromString(argv[2]);
if (from == DCDBUnit_None) {
std::cout << "No known unit: " << argv[2] << std::endl;
return 3;
}
to = UnitConv::fromString(argv[3]);
if (to == DCDBUnit_None) {
std::cout << "No known unit: " << argv[3] << std::endl;
return 4;
}
/* Run conversion */
if (UnitConv::convert(value, from, to)) {
std::cout << value << std::endl;
}
else {
std::cout << "Cannot convert from " << UnitConv::toString(from) << " to " << UnitConv::toString(to) << std::endl;
return 5;
}
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment