-
Notifications
You must be signed in to change notification settings - Fork 10
/
sensor.cpp
311 lines (245 loc) · 9.17 KB
/
sensor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
/*
This file is part of the Automon Project (OBD Diagnostics) - http://www.automon.io/
Source Repository: https://github.com/donaloconnor/automon/
Copyright (c) 2015, Donal O'Connor <donaloconnor@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <QRegExp>
#include "automon.h"
using namespace AutomonKernel;
void Sensor::setFrequency(int frequency)
{
m_maxFrequency = frequency;
}
bool Sensor::isTurn()
{
/*
This method is responsible for checking if it is time to give this sensor access to the ELM.
Depending on the frequency of the sensor, this method will determine how often it will get access.
For example, if frequency is 10Hz, then this will get called every round in the serial thread but only
every 10 times will it be given access to the ELM and processed. This saves on bandwidth. No point updating
coolant temperature every round when it changes slow relative to engine RPM for example.
This method also records the time period between each time it gets processes and gives an average update frequency
time
*/
if (!m_lastTime)
{
// gettimeofday(&m_timeVal, NULL); // [LA]
m_lastTime = m_timeVal.tv_sec+(m_timeVal.tv_usec/1000000.0);
}
if (m_currentFrequency == m_maxFrequency)
{
/* If in here, it is this sensor's turn to get access to the ELM.*/
m_currentFrequency = 1; /* Reset the current frequency */
/* Get the time and do calculations to work out difference from last time */
// gettimeofday(&m_timeVal, NULL); // [LA]
double thisTime = m_timeVal.tv_sec+(m_timeVal.tv_usec/1000000.0);
m_instRefreshRate = 1/(thisTime - m_lastTime);
m_lastTime = thisTime;
if (!m_avgRefreshRate)
m_avgRefreshRate = m_instRefreshRate;
else
m_avgRefreshRate = (m_instRefreshRate + m_avgRefreshRate)/2;
#ifdef DEBUGAUTOMON
qDebug("Avg Refresh Rate: %.6lf Hz", m_instRefreshRate);
#endif
return true;
}
else
{
/* It is not our turn so increment the current frequency so we get closer to having a turn */
m_currentFrequency++;
}
return false;
}
QString Sensor::getName()
{
/* Return the name of this sensor */
return m_englishMeaning;
}
QString Sensor::getPid()
{
return m_command;
}
float Sensor::getAvgRefreshRate()
{
/* Get the average refresh rate */
return m_avgRefreshRate;
}
void Sensor::validateSensorData(QString buffer)
{
/*
This method is responsible for validating the buffer response from the ELM327
*/
/* Take copy of the buffer */
QString data = buffer;
/* Remove spaces and the line break character */
QRegExp removeSpaces( " " );
data.replace(QRegExp("[ \x0D]"), "");
if (data.compare(QString("NODATA>")) == 0 || data.compare(QString("?>")) == 0)
{
/* The no data found message signifies that this sensor was not supported. We shouldn't get to this stage */
#ifdef DEBUGAUTOMON
qDebug() << "Sending: " << getCommand() << " resulted in no data not found!";
#endif
throw nodata_exception();
}
if (data.compare(QString("BUSERROR>")) == 0)
{
/* This occurs when communication breaks down between ELM and ECU */
#ifdef DEBUGAUTOMON
qDebug() << "An error occured when communicating with the bus!";
#endif
throw buserror_exception();
}
if (data.contains(QRegExp("[^a-fA-F0-9 >\x0D]")))
{
/*
If the data contains characters other than a-f, A-f or 0-9, ie: NOT HEX, (excl > and line break)
then response invalid */
#ifdef DEBUGAUTOMON
qDebug() << data;
qDebug() << "The response from the ELM327 was not hexidecimal when it should have being";
#endif
throw nothexdata_exception();
}
}
void Sensor::setBuffer(QString bufferResponse)
{
/* This method is called by the serial I/O thread to set the returned bytes from ELM */
if (bufferResponse.compare(m_bufferResponse) !=0 || m_changeTimes == 0)
{
/* Only if response has changed from last response or if this is our first update (m_changeTimes = 0) */
try
{
/* First validate the data, this will throw exception if data invalid */
validateSensorData(bufferResponse);
/* Now update the buffer to the response received from ELM */
m_bufferResponse = bufferResponse;
/* The set Result method is used to convert the result and look after signaling */
setResult();
}
catch (exception & e)
{
/* Data received was invalid, we choose to ignore it. this can happen easily */
qWarning() << "An exception occured when reading data from device so result won't be recorded";
qWarning() << "The exception was: " << e.what();
}
}
}
int Sensor::getChangeTimes()
{
/* Get the change times. Used in the rules class */
return m_changeTimes;
}
void Sensor::setResult()
{
/* Convert the result to get it's double value */
m_result = convertResult();
if (!m_wasOutOfRange && !checkIfOutOfRange(m_result))
{
/* If not outOfRange before and is out of range now */
m_wasOutOfRange = true; /* Set out of range to true. Prevents us coming in here in consecutive out of range times */
#ifdef DEBUGAUTOMON
qDebug() << "The sensor, " << getEnglishMeaning() << " was out of range with a result of " << QString::number(m_result);
#endif
emit outOfRangeError(QString("The value obtained for <strong>") + getEnglishMeaning() + QString("</strong> is in an <strong><font color=\"red\">unsafe</font></strong> range"));
}
else if (checkIfOutOfRange(m_result))
{
/* Not out of range. (Really the bools should be opposite in the checkIfOutOfRange method, mis leading) */
if (m_wasOutOfRange)
m_wasOutOfRange = false; /* Reset the out of range variable so if we encounter a out of range in future, we deal with it */
/* We only got into this method if a change occured, so now emit the change passing the result */
emit changeOccurred(m_result);
/* Update the change times. WARNING, could end up with overflow of int here if it got big enough */
m_changeTimes++;
}
#ifdef DEBUGAUTOMON
qDebug() << "Emitting Change of result to connected slots";
#endif
}
void Sensor::resetSensor()
{
/* Reset the change times. Important in some things like rules */
m_changeTimes = 0;
}
Sensor::Sensor()
{
/* Sensor constructor, set up defaults for all variables */
setExpectedBytes(0);
setSupported(false);
m_wasOutOfRange = false;
m_maxFrequency = 1;
m_currentFrequency = 1;
m_changeTimes = 0;
}
double Sensor::convertResult()
{
/* If convertResult not implemented by derieved classes, just return 0 */
return 0.0;
}
void Sensor::setMax(double max)
{
/* Set maximum value this sensor can have */
m_maxVal = max;
}
void Sensor::setMin(double min)
{
/* Set minimum value this sensor can have */
m_minVal = min;
}
double Sensor::getMin()
{
/* Get minimum value sensor can have */
return m_minVal;
}
double Sensor::getMax()
{
/* Get maximum value sensor can have */
return m_maxVal;
}
double Sensor::getResult() const
{
/* Get the current value of the sensor */
return m_result;
}
bool Sensor::checkIfOutOfRange(double value)
{
/* If out of range, return false. By right the bools should be opposite. bit mis leading */
if (value < m_minVal || value > m_maxVal)
return false;
return true;
}
void Sensor::setUnits(UNITS resultUnits)
{
/* Set the unit type of this sensor */
m_resultUnits = resultUnits;
}
void Sensor::setSupported(bool isSupported)
{
/* Set support for current sensor on current vehicle */
m_isSupported = isSupported;
}
bool Sensor::isSupported()
{
/* Check if sensor supported */
return m_isSupported;
}