Skip to content

Commit 8d47b45

Browse files
committed
Labee driver prerequisites and additional errors
Message errors for the new error types
1 parent f9c4178 commit 8d47b45

File tree

6 files changed

+245
-31
lines changed

6 files changed

+245
-31
lines changed

Makefile.am

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ endif
6262
if ENABLE_LABEE
6363
lib@PACKAGE_TARNAME@_la_CPPFLAGS += -DENABLE_LABEE
6464
lib@PACKAGE_TARNAME@_la_SOURCES += src/driver-labee.c
65+
66+
lib@PACKAGE_TARNAME@_la_LIBADD += @XML_CURL_LIBS@
67+
lib@PACKAGE_TARNAME@_la_CPPFLAGS += @XML_CURL_CFLAGS@
6568
endif
6669

6770

configure.ac

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
AC_INIT([EML], [0.2.0], [cap@pcg.ull.es])
1+
AC_INIT([EML], [1.0.0], [cap@pcg.ull.es])
22
AC_CONFIG_SRCDIR([src/device.c])
33
AC_CONFIG_HEADERS([config.h])
44
AC_CONFIG_FILES([Makefile Doxyfile])
@@ -68,7 +68,8 @@ AM_CONDITIONAL([ENABLE_ODROID], [test "x$have_odroid" = "xyes"])
6868
AC_ARG_ENABLE([labee], AS_HELP_STRING([--enable-labee],
6969
[Enable Labee support
7070
(default: disable)]))
71-
AS_IF([test "x$enable_labee" = "xyes"], [have_labee=yes])
71+
AS_IF([test "x$enable_labee" = "xyes"],
72+
[PKG_CHECK_MODULES([XML_CURL], [libxml-2.0, libcurl], [have_labee=yes])])
7273
AM_CONDITIONAL([ENABLE_LABEE], [test "x$have_labee" = "xyes"])
7374

7475
LT_INIT

include/eml/error.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ typedef enum emlError {
6161
EML_MEASUREMENT_STACK_FULL = 14,
6262
/** Bad configuration file */
6363
EML_BAD_CONFIG = 15,
64-
/** Network error */
64+
/** Network connection error */
6565
EML_NETWORK_ERROR = 16,
66+
/** Sensor unavailable for reading */
67+
EML_SENSOR_UNAVAILABLE = 17,
68+
/** Sensor responds, but it responds strange values */
69+
EML_SENSOR_MEASUREMENT_ERROR = 18,
6670
/** Internal library error */
6771
EML_UNKNOWN = 999
6872
} emlError_t;

src/driver-dummy.c

+2-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ static enum emlError init(cfg_t* const config) {
4141
assert(config);
4242
dummy_driver.config = config;
4343

44-
printf("OMG pre\n");
4544
dummy_driver.ndevices = 1;
4645
dummy_driver.devices = malloc(dummy_driver.ndevices * sizeof(*dummy_driver.devices));
4746
for (size_t i = 0; i < dummy_driver.ndevices; i++) {
@@ -55,7 +54,6 @@ static enum emlError init(cfg_t* const config) {
5554
memcpy(dev, &devinit, sizeof(*dev));
5655
}
5756

58-
printf("OMG\n");
5957
dummy_driver.initialized = 1;
6058
return EML_SUCCESS;
6159
}
@@ -81,8 +79,8 @@ static enum emlError measure(size_t devno, unsigned long long* values) {
8179
// default measurement properties for this driver
8280
static struct emlDataProperties default_props = {
8381
.time_factor = EML_SI_MILLI,
84-
.energy_factor = EML_SI_MILLI, // Measurement is in W, but to store on a long, it is multiplied by EML_SI_MEGA
85-
// .power_factor = EML_SI_MICRO,
82+
.energy_factor = 1,
83+
.power_factor = EML_SI_MILLI,
8684
.inst_energy_field = 0,
8785
.inst_power_field = 1,
8886
};

src/driver-labee.c

+228-24
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,20 @@
1212
#define _XOPEN_SOURCE 500
1313

1414
#include <assert.h>
15-
//#include <ctype.h>
15+
#include <confuse.h>
16+
#include <curl/curl.h>
17+
#include <ctype.h>
18+
#include <stdlib.h>
19+
#include <string.h>
20+
#include <libxml/parser.h>
21+
#include <libxml/tree.h>
1622
//#include <errno.h>
1723
//#include <stddef.h>
1824
//#include <stdint.h>
19-
//#include <stdio.h>
20-
#include <stdlib.h>
21-
#include <string.h>
25+
2226
//#include <dirent.h>
2327
//
24-
#include <confuse.h>
28+
2529
//#include <fcntl.h>
2630
//#include <sys/stat.h>
2731
//#include <unistd.h>
@@ -32,21 +36,127 @@
3236
#include "error.h"
3337
#include "timer.h"
3438

35-
#define LABEE_DEFAULT_SAMPLING_INTERVAL 100000000L // 100ms
39+
// Confuse CFG options
40+
#define LABEE_HOSTNAME_CFG "hostname"
41+
#define LABEE_NODELIST_FILENAME_CFG "nodelist_file"
42+
#define LABEE_DEFAULT_NODELIST_FILENAME "./nodelist"
43+
#define LABEE_STATUS_CFG "disabled"
44+
#define LABEE_DEFAULT_STATUS cfg_false
45+
#define LABEE_SAMPLING_INTERVAL_CFG "sampling_interval"
46+
#define LABEE_DEFAULT_SAMPLING_INTERVAL 150000000L // 150ms
47+
#define LABEE_API_URL_CFG "api_url"
48+
#define LABEE_API_USER_CFG "user"
49+
#define LABEE_API_PASSWD_CFG "password"
50+
#define LABEE_DEFAULT_API_URL "http://10.11.12.242/REST/node"
51+
52+
#define LABEE_NODE_ID "id"
53+
#define LABEE_DEFAULT_POWER_ATTR "actualPowerUsage"
54+
#define LABEE_POWER_ATTR_CFG "power_attribute"
55+
56+
#define LABEE_NODELIST_DELIM ","
3657

3758
struct emlDriver labee_driver;
3859

60+
struct xml_content {
61+
char * content;
62+
size_t length;
63+
};
64+
65+
66+
char *trimwhitespace(char *str) {
67+
char *end;
68+
69+
// Trim leading space
70+
while(isspace((unsigned char)*str)) str++;
71+
72+
if(*str == 0) // All spaces?
73+
return str;
74+
75+
// Trim trailing space
76+
end = str + strlen(str) - 1;
77+
while(end > str && isspace((unsigned char)*end)) end--;
78+
79+
// Write new null terminator character
80+
end[1] = '\0';
81+
82+
return str;
83+
}
84+
85+
86+
static size_t store_partial_content(void * content, size_t size,
87+
size_t nmemb, struct xml_content* s) {
88+
size_t new_length = s->length + size * nmemb;
89+
s->content = realloc(s->content, new_length + 1);
90+
memcpy(s->content + s->length, content, size*nmemb);
91+
s->length = new_length;
92+
s->content[s->length] = '\0';
93+
return size * nmemb;
94+
}
95+
96+
97+
static enum emlError get_xml(struct xml_content * xc) {
98+
enum emlError err = EML_SUCCESS;
99+
char * labee_api_url = cfg_getstr(labee_driver.config, LABEE_API_URL_CFG);
100+
char * api_user = cfg_getstr(labee_driver.config, LABEE_API_USER_CFG);
101+
char * api_passwd = cfg_getstr(labee_driver.config, LABEE_API_PASSWD_CFG);
102+
CURLcode res;
103+
104+
CURL * curl = curl_easy_init();
105+
106+
if (curl) {
107+
curl_easy_setopt(curl, CURLOPT_URL, labee_api_url);
108+
curl_easy_setopt(curl, CURLOPT_USERNAME, api_user);
109+
curl_easy_setopt(curl, CURLOPT_PASSWORD, api_passwd);
110+
111+
struct curl_slist * list = 0;
112+
list = curl_slist_append(list, "Content-type: application/xml");
113+
114+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)xc);
115+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, store_partial_content);
116+
res = curl_easy_perform(curl);
117+
118+
if (res != CURLE_OK) {
119+
snprintf(labee_driver.failed_reason, sizeof(labee_driver.failed_reason),
120+
"obtain_xml(): %s, %s ", labee_api_url, curl_easy_strerror(res));
121+
err = EML_NETWORK_ERROR;
122+
}
123+
124+
curl_easy_cleanup(curl);
125+
}
126+
return err;
127+
}
128+
129+
130+
void init_xml(struct xml_content ** xc) {
131+
(*xc) = malloc(sizeof(struct xml_content));
132+
(*xc)->content = malloc(1);
133+
(*xc)->content[0] = '\0';
134+
(*xc)->length = 0;
135+
}
136+
137+
138+
void free_xml(struct xml_content * xc) {
139+
if (xc) {
140+
free(xc->content);
141+
xc->length = 0;
142+
free(xc);
143+
}
144+
}
145+
39146

40147
static enum emlError init(cfg_t* const config) {
41148
assert(!labee_driver.initialized);
42149
assert(config);
43150
labee_driver.config = config;
44151

45152
enum emlError err;
46-
47-
// TODO Check if the API is up
153+
// Check that the API is working
154+
struct xml_content * xc;
155+
init_xml(&xc);
156+
err = get_xml(xc);
157+
free_xml(xc);
48158

49-
// FUnction that generates error code
159+
// Function that generates error code
50160
if (err != EML_SUCCESS)
51161
goto err_free;
52162

@@ -73,41 +183,135 @@ static enum emlError init(cfg_t* const config) {
73183
}
74184

75185

76-
static enum emlError shutdown() {
186+
// We were using shutdown of the sake of consistency between all drivers
187+
// but it produces a conflict with a shutdown function used in curl.
188+
// It has been changed as it does not affect any behaviour.
189+
static enum emlError labee_shutdown() {
77190
assert(labee_driver.initialized);
78191

79192
labee_driver.initialized = 0;
80193

81194
return EML_SUCCESS;
82195
}
83196

197+
198+
static enum emlError obtain_hostname_rest_reference(char ** ref) {
199+
char * nodelist_filename = cfg_getstr(labee_driver.config, LABEE_NODELIST_FILENAME_CFG);
200+
char * hostname = cfg_getstr(labee_driver.config, LABEE_HOSTNAME_CFG);
201+
char buffer[BUFSIZ];
202+
enum emlError err = EML_BAD_CONFIG;
203+
204+
FILE * fp = fopen(nodelist_filename, "r");
205+
while(fscanf(fp, "%s", buffer) != EOF) {
206+
char * tok1 = strtok(buffer, LABEE_NODELIST_DELIM);
207+
char * tok2 = strtok(0, LABEE_NODELIST_DELIM);
208+
tok2 = trimwhitespace(tok2);
209+
210+
if (!strcmp(tok2, hostname)) {
211+
err = EML_SUCCESS;
212+
(*ref) = malloc(sizeof(tok1));
213+
strcpy(*ref, tok1);
214+
break;
215+
}
216+
}
217+
fclose(fp);
218+
return err;
219+
}
220+
221+
222+
enum emlError get_power_from_xml(long long unsigned * power,
223+
struct xml_content * xc) {
224+
xmlDocPtr doc;
225+
xmlChar * id;
226+
xmlChar * chr_power;
227+
char * ref;
228+
enum emlError err;
229+
const xmlChar * label_id = xmlCharStrdup(LABEE_NODE_ID);
230+
const xmlChar * label_powerusage = xmlCharStrdup(
231+
cfg_getstr(labee_driver.config, LABEE_POWER_ATTR_CFG)
232+
);
233+
234+
err = obtain_hostname_rest_reference(&ref);
235+
if (err != EML_SUCCESS)
236+
return err;
237+
238+
// The document being in memory, it have no base per RFC 2396,
239+
// and the "noname.xml" argument will serve as its base.
240+
doc = xmlReadMemory(xc->content, xc->length, "noname.xml", NULL, 0);
241+
if (!doc)
242+
return EML_SENSOR_MEASUREMENT_ERROR;
243+
244+
xmlNodePtr cur = xmlDocGetRootElement(doc);
245+
cur = cur->children;
246+
while (cur != NULL) {
247+
id = xmlGetProp(cur, label_id);
248+
if (!strcmp((char *) id, ref)) {
249+
xmlFree(id);
250+
chr_power = xmlGetProp(cur, label_powerusage);
251+
break;
252+
}
253+
xmlFree(id);
254+
cur = cur->next;
255+
}
256+
257+
// EML_SI_MEGA is applied since the measurement offers enough precision
258+
(*power) = atof((char *) chr_power) * EML_SI_MEGA;
259+
260+
xmlFreeDoc(doc);
261+
free(ref);
262+
return EML_SUCCESS;
263+
}
264+
265+
84266
static enum emlError measure(size_t devno, unsigned long long* values) {
85267
assert(labee_driver.initialized);
86268
assert(devno < labee_driver.ndevices); // Shouldn't be more than one anyways
87269

88-
enum emlError err;
270+
enum emlError err = EML_SUCCESS;
271+
unsigned long long power;
89272

90-
values[0] = millitimestamp();
91-
const long sampling_interval = cfg_getint(labee_driver.config, "sampling_interval");
92-
unsigned long long power = 1 / sampling_interval;
273+
struct xml_content * xc;
274+
init_xml(&xc);
275+
err = get_xml(xc);
276+
if (err != EML_SUCCESS)
277+
goto err_free;
93278

94-
//TODO parse xml from rest api apply average proportionally to interval
95-
values[labee_driver.default_props->inst_energy_field * DATABLOCK_SIZE] = power;
279+
err = get_power_from_xml(&power, xc);
280+
if (err != EML_SUCCESS)
281+
goto err_free;
282+
283+
values[0] = nanotimestamp();
284+
values[labee_driver.default_props->inst_power_field * DATABLOCK_SIZE] = power;
285+
286+
free_xml(xc);
96287
return EML_SUCCESS;
288+
289+
err_free:
290+
if (labee_driver.failed_reason[0] == '\0')
291+
strncpy(labee_driver.failed_reason, emlErrorMessage(err),
292+
sizeof(labee_driver.failed_reason) - 1);
293+
free_xml(xc);
294+
return err;
97295
}
98296

99297
// default measurement properties for this driver
100298
static struct emlDataProperties default_props = {
101-
.time_factor = EML_SI_MILLI,
102-
.energy_factor = EML_SI_MICRO, // Measurement is in J, but to store on a long, it is multiplied by EML_SI_MEGA
103-
.power_factor = EML_SI_MICRO, // Measurement is in W, same principle, with EML_SI_MEGA
104-
.inst_energy_field = 1,
105-
.inst_power_field = 2,
299+
.time_factor = EML_SI_NANO,
300+
.energy_factor = EML_SI_MICRO,
301+
.power_factor = EML_SI_MICRO, // The unit is in W, so it is saved multiplied by EML_SI_MEGA.
302+
.inst_energy_field = 0,
303+
.inst_power_field = 1,
106304
};
107305

108306
static cfg_opt_t cfgopts[] = {
109-
CFG_BOOL("disabled", cfg_false, CFGF_NONE),
110-
CFG_INT("sampling_interval", LABEE_DEFAULT_SAMPLING_INTERVAL, CFGF_NONE),
307+
CFG_BOOL(LABEE_STATUS_CFG, LABEE_DEFAULT_STATUS, CFGF_NONE),
308+
CFG_INT(LABEE_SAMPLING_INTERVAL_CFG, LABEE_DEFAULT_SAMPLING_INTERVAL, CFGF_NONE),
309+
CFG_STR(LABEE_API_URL_CFG, LABEE_DEFAULT_API_URL, CFGF_NONE),
310+
CFG_STR(LABEE_HOSTNAME_CFG, "", CFGF_NONE),
311+
CFG_STR(LABEE_NODELIST_FILENAME_CFG, "./nodelist", CFGF_NONE),
312+
CFG_STR(LABEE_API_USER_CFG, "", CFGF_NONE),
313+
CFG_STR(LABEE_API_PASSWD_CFG, "", CFGF_NONE),
314+
CFG_STR(LABEE_POWER_ATTR_CFG, LABEE_DEFAULT_POWER_ATTR, CFGF_NONE),
111315
CFG_END()
112316
};
113317

@@ -121,6 +325,6 @@ struct emlDriver labee_driver = {
121325
.config = NULL,
122326

123327
.init = &init,
124-
.shutdown = &shutdown,
328+
.shutdown = &labee_shutdown,
125329
.measure = &measure,
126330
};

src/error.c

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ const char* emlErrorMessage(enum emlError err) {
4545
return "malformed configuration file";
4646
case EML_NETWORK_ERROR:
4747
return "network error";
48+
case EML_SENSOR_UNAVAILABLE:
49+
return "Measurement sensor not present";
50+
case EML_SENSOR_MEASUREMENT_ERROR:
51+
return "Measurement sensor present, but seems not to work properly";
4852
default:
4953
return "unknown error";
5054
}

0 commit comments

Comments
 (0)