#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <math.h>
#include "parser.h"
#include "debug.h"
#include "utils.h"
#include "plugin.h"
#include "version.h"
+#include "utils.h"
+
+struct owparser_state {
+ double prev_delta[20];
+ double prev_data;
+};
+
+static struct owparser_state *allocate_parser_state(const char **datastr)
+{
+ int i;
+
+ /* Count how many sensor entries we need */
+ for (i = 0; datastr[i]; i++)
+ ;
+
+ /* The first entry belongs to server address or mount point */
+ i--;
+
+ return calloc(sizeof(struct owparser_state), i);
+}
+
+static int might_be_glitch(double data, const struct owparser_state *s)
+{
+ double max_delta = 0, delta;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->prev_delta); i++)
+ max_delta = max(s->prev_delta[i], max_delta);
+
+ /* Probably no enough data yet, so no glitch detection */
+ if (!max_delta)
+ return 0;
+
+ /*
+ * Simple glitch detection. If delta to previous value is more
+ * than twice as larger as some older delta, we might have a
+ * glit
+ */
+ delta = fabs(data - s->prev_data);
+ return delta > max_delta * 2;
+}
+
+static void update_glitch_data(double data, struct owparser_state *s)
+{
+ double max_delta = 0;
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(s->prev_delta); i++) {
+ s->prev_delta[i - 1] = s->prev_delta[i];
+ max_delta = max(s->prev_delta[i], max_delta);
+ }
-static int parse_opts(const char *str, char *ow_path, size_t pathlen, double *offset)
+ /* Avoid storing the first incorrect delta value */
+ if (s->prev_data || max_delta)
+ s->prev_delta[--i] = fabs(data - s->prev_data);
+
+ s->prev_data = data;
+}
+
+static int parse_opts(const char *str, char *ow_path, size_t pathlen,
+ double *offset)
{
char *endptr;
const char *start_str = str;
{
OWNET_HANDLE h;
const char *server_addr, *mount_point;
+ struct owparser_state *state = *s;
char buf[24], *tmp;
int i = 1, ret;
int max_str = RRD_DATA_MAX_LEN;
return -1;
}
+ if (!state)
+ *s = state = allocate_parser_state(parser_data);
+
if (is_mountpoint) {
mount_point = parser_data[0];
}
while (parser_data[i]) {
- double offset = 0, data;
+ double offset = 0, data, prev_data = 85;
char *endptr;
char ow_path[1024];
int retries = 0;
+ int glitches = 0;
if (!strcmp("U", parser_data[i])) {
undefined:
else
fail = 1;
+ if (ret <= 0 || fail)
+ goto retry;
+
+
+ /*
+ * Older versions of OWNET_read did not NULL
+ * terminate data.
+ */
+ memcpy(buf, tmp, min(ret, sizeof(buf) -1));
+ buf[ret] = 0;
+
+ data = strtod(buf, &endptr);
+
+ free(tmp);
+ tmp = NULL;
+
+ /*
+ * If we read the same value as previously,
+ * it's not a glitch
+ */
+ if (glitches && prev_data == data)
+ break;
+
+ if (might_be_glitch(data, &state[i]) &&
+ glitches < 2 && retries < 7) {
+ glitches++;
+ prev_data = data;
+ pr_info("Retrying due to a glitch: %f\n", data);
+ goto retry;
+ }
+
+ break;
+retry:
/*
* In case of failure, retry with uncached
* data. This is likely to help as it forces a
* retry even if the sensor is missing from
- * the cache. We thread "85" also as a failure
- * above, as temp sensors some times report 85
- * under faulty conditions.
+ * the cache. We treat "85" also as a failure,
+ * as temp sensors some times report 85 under
+ * faulty conditions.
*/
- if (ret > 0 && !fail)
- break;
-
ret = make_uncached(ow_path, sizeof(ow_path));
if (retries >= 10 || ret < 0) {
pr_err("Failed to read entry %s: %m\n",
free(tmp);
}
- /* The data from OWNET_read appears to not be NULL terminated */
- memcpy(buf, tmp, min(ret, sizeof(buf) -1));
- free(tmp);
- buf[ret] = 0;
-
- data = strtod(buf, &endptr);
+ update_glitch_data(data, &state[i]);
if (endptr == buf) {
pr_err("Failed to parse data %s\n", buf);