sensorcache.cpp 5.54 KB
Newer Older
1
2
3
4
5
6
7
8
/*
 * SensorCache.cpp
 *
 *  Created on: 3 Aug 2016
 *      Author: ottmi
 */

#include "sensorcache.h"
9
#include <dcdb/timestamp.h>
10
11
12

#include <exception>
#include <iostream>
13
14
15
#include <iomanip>
#include <algorithm>

16
17
18
19
20
21
22
23
24
25
26
27
namespace DCDB {

SensorCache::SensorCache() {
	// TODO Auto-generated constructor stub

}

SensorCache::~SensorCache() {
	// TODO Auto-generated destructor stub
}

void SensorCache::storeSensor(SensorId sid, uint64_t ts, uint64_t val) {
28
29
	cacheEntry_t e;

30
31
	/* Remove the reserved bytes to leverage the standard find function */
	sid.setRsvd(0);
32
33
34
	sensorCache_t::iterator it = sensorCache.find(sid);
	if (it != sensorCache.end()) {
		e = it->second;
35
36
		if (e.front().timestamp+MAX_HISTORY_NS > ts) {
		  e.pop_front();
37
38
39
		}
	}

40
41
        sensorReading_t s = { val, ts };
        sensorCache[sid].push_back(s);
42
43
}

44
bool SensorCache::checkValid(cacheEntry_t &entry) {
45
46
  if (entry.size() > 2) {
    TimeStamp ts;
47
    uint64_t avg = 0;
48
49
50
51
52
    cacheEntry_t::iterator it = entry.begin();
    uint64_t prev = it->timestamp;
    for (it++; it != entry.end(); it++) {
      avg+= it->timestamp - prev;
      prev = it->timestamp;
53
    }
54
    avg/= (entry.size()-1);
55
56
57
58

    /*
     * A SID is outdated if it's older than 5x the average sampling period.
     */
59
    if ((ts.getRaw() - entry.back().timestamp) > 5 * avg) {
60
61
62
63
64
65
66
      return false;
    }
  }

  return true;
}

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
int64_t SensorCache::getAverage(cacheEntry_t &entry, uint64_t avg) {
  TimeStamp ts;

  if (entry.size() > 0) {
    //std::cout << ts.getRaw() - entry.back().timestamp << std::endl;
    if (ts.getRaw() - entry.back().timestamp > avg * NS_PER_S) {
      throw std::out_of_range("Sid outdated");
      return 0;
    }

    double sum = 0;
    cacheEntry_t::reverse_iterator it, prev;
    it = prev = entry.rbegin();
    it++;
    while ((it != entry.rend()) && ((ts.getRaw() - it->timestamp) <= avg * NS_PER_S)) {
      uint64_t deltaT = (prev->timestamp - it->timestamp);
      sum+= ((it->val + prev->val) / 2) * deltaT;
      //std::cout << "SensorCache::getAverage sum=" << sum << " deltaT=" <<deltaT << " it=(" << it->timestamp << "," <<it->val <<") prev=(" <<  prev->timestamp << "," << prev->val <<") " << (ts.getRaw() - it->timestamp) << std::endl;
      prev = it++;
    }

    //std::cout << "SensorCache::getAverage (" << prev->timestamp << "," <<prev->val <<") (" <<  entry.back().timestamp << "," << entry.back().val << ") sum=" << sum << " deltaT=" << entry.back().timestamp - prev->timestamp << std::endl;
    if (prev == entry.rbegin()) {
      return entry.back().val;
    } else {
      return sum/(entry.back().timestamp - prev->timestamp);
    }
  }
  throw std::invalid_argument("Sid not found");
  return 0;
}

uint64_t SensorCache::getSensor(SensorId sid, uint64_t avg) {
100
101
102
  /* Remove the reserved bytes to leverage the standard find function */
  sid.setRsvd(0);
  sensorCache_t::iterator it = sensorCache.find(sid);
103

104
105
  if (it == sensorCache.end()) {
    throw std::invalid_argument("Sid not found");
106
    return 0;
107
  }
108

109
110
111
  if (!checkValid(it->second))
  {
    throw std::out_of_range("Sid outdated");
112
    return 0;
113
114
  }

115
116
117
118
119
  if (avg) {
    return getAverage(it->second, avg);
  } else {
    return it->second.back().val;
  }
120
}
121

122
uint64_t SensorCache::getSensor(std::string topic, uint64_t avg) {
123
124
125
126
  topic.erase(std::remove(topic.begin(), topic.end(), '/'), topic.end());

  size_t wp = topic.find("*");
  if (wp == std::string::npos) {
127
    return getSensor(DCDB::SensorId(topic), avg);
128
129
130
131
132
133
134
  }

  int wl = 33 - topic.length();

  /* Create SensorIds with the lowest and highest values matching the wildcard */
  DCDB::SensorId sidLow(std::string(topic).replace(wp, 1, std::string(wl, '0')));
  DCDB::SensorId sidHi(std::string(topic).replace(wp, 1, std::string(wl, 'f')));
135
  DCDB::SensorId sidMask(std::string(wp, 'f') + std::string(wl, '0') + std::string(topic.length()-wp-1, 'f'));
136
137
138
139
140
  sidLow.setRsvd(0);
  sidHi.setRsvd(0);

  /* See whether there's a SensorId in the cache >= sidLow */
  sensorCache_t::iterator it = sensorCache.lower_bound(sidLow);
141
  sensorCache_t::iterator mostRecentSidIt = sensorCache.end();
142
143

  bool foundOne = false;
144
145
146
147
148
149
150
  /* Iterate over the cache until the current entry is > sidHi */
  while ((it != sensorCache.end()) && (it->first <= sidHi)) {
    if ((it->first & sidMask) == sidLow) {
      foundOne = true;
      /* We only return one value, even if multiple SensorIds would match.
       * At least make sure it's the most recent value
       */
151
      if (checkValid(it->second) && ((mostRecentSidIt == sensorCache.end()) || mostRecentSidIt->second.back().timestamp < it->second.back().timestamp)) {
152
153
        mostRecentSidIt = it;
      }
154
155
156
157
158
159
160
161
162
163
164
165
166
    }
    it++;
  }

  if (mostRecentSidIt == sensorCache.end()) {
    /* Check whether we actually found at least an outdated entry */
    if (foundOne) {
      throw std::out_of_range("Sid outdated");
    } else {
      throw std::invalid_argument("Sid not found");
    }
  }

167
168
169
170
171
  if (avg) {
    return getAverage(mostRecentSidIt->second, avg);
  } else {
    return mostRecentSidIt->second.back().val;
  }
172
173
174
}

void SensorCache::dump() {
175
176
177
178
179
180
181
182
183
184
185
186
187
  std::cout << "SensorCache Dump:" << std::endl;
  for (sensorCache_t::iterator sit = sensorCache.begin(); sit != sensorCache.end(); sit++) {
    std::cout << "  id=" << sit->first.toString() << " data=[";
    for (cacheEntry_t::iterator eit = sit->second.begin(); eit != sit->second.end(); eit++) {
      if (eit != sit->second.begin()) {
        std::cout << ",";
      }
      std::cout << "(" <<  eit->val << "," << eit->timestamp/NS_PER_S << "." << std::setfill ('0') << std::setw (9) << eit->timestamp%NS_PER_S << ")";
    }
    std::cout << "]" << std::endl;
  }


188
189
190
}

} /* namespace DCDB */