add sensor framework, tests (#929)
* add framework, tests * move oil pressure to new way * add init logic * brackets on the same line * spaces -> tabs * spaces -> tabs for tests * bracket on same line * hook up sensor mocking * add nan check * fix nan check * I wrote an essay * casing * only init if we have a sensor to init * style, actually call init * format * fix casing * typo * implement linear sensor * wire up producer * smarter limiting * setup comments * add reporting * doxyfile * oops * add adc subscription * clarity * fix logic * multiply voltage * test styling * test guards * remove dependencies * linear sensor test * remove unused * fix merge * format, implicit convert op * explicit * format tests * fix merge
This commit is contained in:
parent
f3c82eec0c
commit
f629ec038b
|
@ -742,6 +742,7 @@ INPUT = . \
|
|||
util \
|
||||
console \
|
||||
controllers \
|
||||
init \
|
||||
emulation \
|
||||
hw_layer
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@ include $(PROJECT_DIR)/controllers/sensors/sensors.mk
|
|||
include $(PROJECT_DIR)/controllers/system/system.mk
|
||||
include $(PROJECT_DIR)/controllers/trigger/trigger.mk
|
||||
include $(PROJECT_DIR)/console/console.mk
|
||||
include $(PROJECT_DIR)/init/init.mk
|
||||
|
||||
ifeq ($(BOOTLOADERINC),)
|
||||
# include default bootloader code
|
||||
|
@ -229,6 +230,7 @@ CPPSRC = $(CHCPPSRC) \
|
|||
$(UTILSRC_CPP) \
|
||||
$(CONTROLLERS_CORE_SRC_CPP) \
|
||||
$(CONTROLLERS_MATH_SRC_CPP) \
|
||||
$(INIT_SRC_CPP) \
|
||||
rusefi.cpp \
|
||||
main.cpp
|
||||
|
||||
|
@ -297,6 +299,7 @@ INCDIR = $(CHIBIOS)/os/license \
|
|||
$(HW_INC) \
|
||||
$(HW_LAYER_DRIVERS_INC) \
|
||||
$(UTIL_INC) \
|
||||
init \
|
||||
development \
|
||||
development/hw_layer \
|
||||
development/test \
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "trigger_central.h"
|
||||
#include "allsensors.h"
|
||||
#include "sensor_reader.h"
|
||||
#include "io_pins.h"
|
||||
#include "efi_gpio.h"
|
||||
#include "mmc_card.h"
|
||||
|
@ -782,8 +783,6 @@ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels DECLARE_
|
|||
tsOutputChannels->accelerationX = engine->sensors.accelerometer.x;
|
||||
// 278
|
||||
tsOutputChannels->accelerationY = engine->sensors.accelerometer.y;
|
||||
// 280
|
||||
tsOutputChannels->oilPressure = engine->sensors.oilPressure;
|
||||
// 288
|
||||
tsOutputChannels->injectionOffset = engine->engineState.injectionOffset;
|
||||
|
||||
|
|
|
@ -139,7 +139,6 @@ void EngineState::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
|||
engine->sensors.clt = engine->sensors.mockClt;
|
||||
}
|
||||
#endif
|
||||
engine->sensors.oilPressure = getOilPressure(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||
}
|
||||
|
||||
void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||
|
|
|
@ -63,11 +63,6 @@ public:
|
|||
float auxTemp1 = NAN;
|
||||
float auxTemp2 = NAN;
|
||||
|
||||
/**
|
||||
* Oil pressure in kPa
|
||||
*/
|
||||
float oilPressure;
|
||||
|
||||
Accelerometer accelerometer;
|
||||
|
||||
float vBatt = 0;
|
||||
|
|
|
@ -550,6 +550,7 @@ typedef enum {
|
|||
//P0518 Idle Air Control Circuit Intermittent
|
||||
//P0519 Idle Air Control System Performance
|
||||
//P0520 Engine Oil Pressure Sensor/Switch Circuit Malfunction
|
||||
OBD_Oil_Pressure_Sensor_Malfunction = 520,
|
||||
//P0521 Engine Oil Pressure Sensor/Switch Circuit Range/Performance
|
||||
//P0522 Engine Oil Pressure Sensor/Switch Circuit Low Voltage
|
||||
//P0523 Engine Oil Pressure Sensor/Switch Circuit High Voltage
|
||||
|
|
|
@ -120,6 +120,7 @@ typedef void (*VoidFloatFloat)(float, float);
|
|||
typedef void (*VoidFloatFloatVoidPtr)(float, float, void*);
|
||||
typedef void (*VoidIntInt)(int, int);
|
||||
typedef void (*VoidIntIntVoidPtr)(int, int, void*);
|
||||
typedef void (*VoidIntFloat)(int, float);
|
||||
|
||||
typedef void (*VoidCharPtr)(const char *);
|
||||
typedef void (*VoidCharPtrVoidPtr)(const char *, void*);
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#if EFI_PROD_CODE
|
||||
#include "pwm_generator.h"
|
||||
#include "adc_inputs.h"
|
||||
#include "init.h"
|
||||
|
||||
#include "pwm_tester.h"
|
||||
#include "pwm_generator.h"
|
||||
|
@ -90,6 +91,10 @@
|
|||
|
||||
// this method is used by real firmware and simulator and unit test
|
||||
void mostCommonInitEngineController(Logging *sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
#if !EFI_UNIT_TEST
|
||||
initSensors();
|
||||
#endif
|
||||
|
||||
initSensors(sharedLogger PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
|
||||
initAccelEnrichment(sharedLogger PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "ego.h"
|
||||
#include "voltage.h"
|
||||
#include "thermistors.h"
|
||||
#include "oil_pressure.h"
|
||||
#include "adc_inputs.h"
|
||||
|
||||
#include "adc_inputs.h"
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* @file converter_sensor.h
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stored_value_sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Base class for sensors that convert from some raw floating point
|
||||
* value (ex: voltage, frequency, pulse width) to a sensor reading.
|
||||
*
|
||||
* To use this base class, inherit it and implement ConvertFromInputValue(float input).
|
||||
* Perform any conversion work necessary to convert from the raw value to a sensor
|
||||
* reading, and return it. Register an instance of the new class with an interface
|
||||
* that provides and posts raw values so the sensor can update.
|
||||
*/
|
||||
class ConvertedSensor : public StoredValueSensor {
|
||||
public:
|
||||
void postRawValue(float inputValue) {
|
||||
// Report the raw value
|
||||
float *rawReportLocation = m_rawReportingLocation;
|
||||
if (rawReportLocation) {
|
||||
*rawReportLocation = inputValue;
|
||||
}
|
||||
|
||||
auto r = convertFromInputValue(inputValue);
|
||||
|
||||
// This has to happen so that we set the valid bit after
|
||||
// the value is stored, to prevent the data race of reading
|
||||
// an old invalid value
|
||||
if (r.Valid) {
|
||||
setValidValue(r.Value);
|
||||
} else {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void setRawReportingLocation(float *rawReportingLocation) {
|
||||
m_rawReportingLocation = rawReportingLocation;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit ConvertedSensor(SensorType type)
|
||||
: StoredValueSensor(type) {}
|
||||
|
||||
/**
|
||||
* @brief Convert from the "raw" input value to a sensor reading (or invalid).
|
||||
*
|
||||
* For example, this function might convert from a voltage to the pressure
|
||||
* represented by that voltage.
|
||||
*/
|
||||
virtual SensorResult convertFromInputValue(float inputValue) = 0;
|
||||
|
||||
private:
|
||||
float *m_rawReportingLocation = nullptr;
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* @file function_pointer_sensor.h
|
||||
* @brief A sensor to provide a bridge from old getX()-style functions to the new sensor registry.
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/* This class is intended as a bridge to bridge from old getMySensor() functions
|
||||
* to the new system. This way, producers and consumers can be independently
|
||||
* updated to the new system, with sensors being usable either way for some time.
|
||||
*/
|
||||
class FunctionPointerSensor final : public Sensor {
|
||||
public:
|
||||
FunctionPointerSensor(SensorType type, float (*func)())
|
||||
: Sensor(type)
|
||||
, m_func(func) {}
|
||||
|
||||
SensorResult get() const final {
|
||||
float result = m_func();
|
||||
|
||||
// check for NaN
|
||||
bool valid = !(result != result);
|
||||
|
||||
return {valid, result};
|
||||
}
|
||||
|
||||
private:
|
||||
float (*m_func)();
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
#include "linear_sensor.h"
|
||||
|
||||
#include "interpolation.h"
|
||||
|
||||
void LinearSensor::configure(float in1, float out1, float in2, float out2, float minOutput, float maxOutput) {
|
||||
m_minOutput = minOutput;
|
||||
m_maxOutput = maxOutput;
|
||||
|
||||
m_a = INTERPOLATION_A(in1, out1, in2, out2);
|
||||
m_b = out1 - m_a * in1;
|
||||
}
|
||||
|
||||
SensorResult LinearSensor::convertFromInputValue(float inputValue) {
|
||||
float result = m_a * inputValue + m_b;
|
||||
|
||||
// Bounds check
|
||||
bool isValid = result <= m_maxOutput && result >= m_minOutput;
|
||||
|
||||
return {isValid, result};
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "converter_sensor.h"
|
||||
|
||||
class LinearSensor final : public ConvertedSensor {
|
||||
public:
|
||||
explicit LinearSensor(SensorType type)
|
||||
: ConvertedSensor(type) {}
|
||||
|
||||
void configure(float in1, float out1, float in2, float out2, float minOutput, float maxOutput);
|
||||
|
||||
protected:
|
||||
SensorResult convertFromInputValue(float inputValue) override;
|
||||
|
||||
private:
|
||||
// Linear equation parameters for equation of form
|
||||
// y = ax + b
|
||||
float m_a = 1;
|
||||
float m_b = 0;
|
||||
|
||||
float m_minOutput = 0;
|
||||
float m_maxOutput = 0;
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* @author Matthew Kennedy, (c) 2017
|
||||
*/
|
||||
#include "global.h"
|
||||
#include "os_access.h"
|
||||
#include "oil_pressure.h"
|
||||
#include "interpolation.h"
|
||||
#include "adc_inputs.h"
|
||||
#include "engine.h"
|
||||
|
||||
EXTERN_ENGINE;
|
||||
|
||||
bool hasOilPressureSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||
return engineConfiguration->oilPressure.hwChannel != EFI_ADC_NONE;
|
||||
}
|
||||
|
||||
float getOilPressure(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||
// If there's no sensor, return 0 pressure.
|
||||
if(!hasOilPressureSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
oil_pressure_config_s* sensor = &CONFIG(oilPressure);
|
||||
|
||||
float volts = getVoltageDivided("oilp", sensor->hwChannel);
|
||||
|
||||
return interpolateMsg("oil", sensor->v1, sensor->value1, sensor->v2, sensor->value2, volts);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* @date Nov 9, 2017
|
||||
* @author Matthew Kennedy, (c) 2017
|
||||
*/
|
||||
|
||||
#ifndef OIL_PRESSURE_H_
|
||||
#define OIL_PRESSURE_H_
|
||||
|
||||
#include "global.h"
|
||||
#include "engine_configuration.h"
|
||||
|
||||
float getOilPressure(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
bool hasOilPressureSensor(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,116 @@
|
|||
#include "sensor.h"
|
||||
|
||||
// This struct represents one sensor in the registry.
|
||||
// It stores whether the sensor should use a mock value,
|
||||
// the value to use, and if not a pointer to the sensor that
|
||||
// can provide a real value.
|
||||
struct SensorRegistryEntry {
|
||||
bool useMock;
|
||||
float mockValue;
|
||||
Sensor *sensor;
|
||||
};
|
||||
|
||||
static SensorRegistryEntry s_sensorRegistry[static_cast<size_t>(SensorType::PlaceholderLast)] = {};
|
||||
|
||||
bool Sensor::Register() {
|
||||
// Get a ref to where we should be
|
||||
auto &entry = s_sensorRegistry[getIndex()];
|
||||
|
||||
// If there's somebody already here - a consumer tried to double-register a sensor
|
||||
if (entry.sensor) {
|
||||
// This sensor has already been registered. Don't re-register it.
|
||||
return false;
|
||||
} else {
|
||||
// put ourselves in the registry
|
||||
s_sensorRegistry[getIndex()].sensor = this;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void Sensor::resetRegistry() {
|
||||
constexpr size_t len = sizeof(s_sensorRegistry) / sizeof(s_sensorRegistry[0]);
|
||||
|
||||
// Clear all entries
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto &entry = s_sensorRegistry[i];
|
||||
|
||||
entry.sensor = nullptr;
|
||||
entry.mockValue = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ SensorRegistryEntry *Sensor::getEntryForType(SensorType type) {
|
||||
size_t index = getIndex(type);
|
||||
// Check that we didn't get garbage
|
||||
if (index >= getIndex(SensorType::PlaceholderLast)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &s_sensorRegistry[index];
|
||||
}
|
||||
|
||||
/*static*/ const Sensor *Sensor::getSensorOfType(SensorType type) {
|
||||
auto entry = getEntryForType(type);
|
||||
return entry ? entry->sensor : nullptr;
|
||||
}
|
||||
|
||||
/*static*/ SensorResult Sensor::get(SensorType type) {
|
||||
const auto entry = getEntryForType(type);
|
||||
|
||||
// Check if this is a valid sensor entry
|
||||
if (!entry) {
|
||||
return {false, 0.0f};
|
||||
}
|
||||
|
||||
// Next check for mock
|
||||
if (entry->useMock) {
|
||||
return {true, entry->mockValue};
|
||||
}
|
||||
|
||||
// Get the sensor out of the entry
|
||||
const Sensor *s = entry->sensor;
|
||||
if (s) {
|
||||
// If we found the sensor, ask it for a result.
|
||||
return s->get();
|
||||
}
|
||||
|
||||
// We've exhausted all valid ways to return something - sensor not found.
|
||||
return {false, 0};
|
||||
}
|
||||
|
||||
/*static*/ void Sensor::setMockValue(SensorType type, float value) {
|
||||
auto entry = getEntryForType(type);
|
||||
|
||||
if (entry) {
|
||||
entry->mockValue = value;
|
||||
entry->useMock = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void Sensor::setMockValue(int type, float value) {
|
||||
// bounds check
|
||||
if (type <= 0 || type >= static_cast<int>(SensorType::PlaceholderLast)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMockValue(static_cast<SensorType>(type), value);
|
||||
}
|
||||
|
||||
/*static*/ void Sensor::resetMockValue(SensorType type) {
|
||||
auto entry = getEntryForType(type);
|
||||
|
||||
if (entry) {
|
||||
entry->useMock = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void Sensor::resetAllMocks() {
|
||||
constexpr size_t len = sizeof(s_sensorRegistry) / sizeof(s_sensorRegistry[0]);
|
||||
|
||||
// Reset all mocks
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
auto &entry = s_sensorRegistry[i];
|
||||
|
||||
entry.useMock = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* @file sensor.h
|
||||
* @brief Base class for sensors. Inherit this class to implement a new type of sensor.
|
||||
*
|
||||
* This file defines the basis for all sensor inputs to the ECU, and provides a registry
|
||||
* so that consumers may be agnostic to how each sensor may work.
|
||||
*
|
||||
* HOW TO ADD A NEW SENSOR TYPE:
|
||||
*
|
||||
* 1. Add an entry to the enum in sensor_type.h. Be sure to add it ABOVE the placeholder
|
||||
* at the end of the list.
|
||||
*
|
||||
* 2. In the init/sensor folder, create/modify logic to create an instance of the new sensor,
|
||||
* configure it if necessary, and call its Register() function if it should be enabled.
|
||||
* See init_oil_pressure.cpp for a minimal example.
|
||||
*
|
||||
* 3. Consume the new sensor with instance(s) of SensorConsumer<SensorType::MyNewSensor>
|
||||
*
|
||||
* Consumers:
|
||||
*
|
||||
* tl;dr: Use a SensorConsumer. See sensor_consumer.h
|
||||
*
|
||||
* All a consumer does is look up whether a particular sensor is present in the table,
|
||||
* and if so, asks it for the current reading. This could synchronously perform sensor
|
||||
* acquisition and conversion (not recommended), or use a previously stored value (recommended!).
|
||||
* This functionality is implemented in sensor_consumer.h, and sensor.cpp.
|
||||
*
|
||||
* Providers:
|
||||
* Instantiate a subclass of Sensor, and implement the Get() function.
|
||||
* Call Register() to install the new sensor in the registry, preparing it for use.
|
||||
*
|
||||
* Mocking:
|
||||
* The sensor table supports mocking each sensors value. Call Sensor::SetMockValue to
|
||||
* set a mock value for a particular sensor, and Sensor::ResetMockValue or
|
||||
* Sensor::ResetAllMocks to reset one or all stored mock values.
|
||||
*
|
||||
* Composite Sensors:
|
||||
* Some sensors may be implemented as composite sensors, ie sensors that depend on other
|
||||
* sensors to determine their reading. For example, the throttle pedal may have a pair of
|
||||
* potentiometers that provide redundancy for the pedal's position. Each one may be its
|
||||
* own sensor, then with one "master" sensors that combines the results of the others, and
|
||||
* provides validation of whether the readings agree.
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sensor_type.h"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
struct SensorResult {
|
||||
const bool Valid;
|
||||
const float Value;
|
||||
};
|
||||
|
||||
// Fwd declare - nobody outside of Sensor.cpp needs to see inside this type
|
||||
struct SensorRegistryEntry;
|
||||
|
||||
class Sensor {
|
||||
public:
|
||||
// Register this sensor in the sensor registry.
|
||||
// Returns true if registration succeeded, or false if
|
||||
// another sensor of the same type is already registered.
|
||||
// The return value should not be ignored: no error handling/reporting is
|
||||
// done internally!
|
||||
[[nodiscard]] bool Register();
|
||||
|
||||
// Remove all sensors from the sensor registry - tread carefully if you use this outside of a unit test
|
||||
static void resetRegistry();
|
||||
|
||||
/*
|
||||
* Static helper for sensor lookup
|
||||
*/
|
||||
static const Sensor *getSensorOfType(SensorType type);
|
||||
|
||||
/*
|
||||
* Get a reading from the specified sensor.
|
||||
*/
|
||||
static SensorResult get(SensorType type);
|
||||
|
||||
/*
|
||||
* Mock a value for a particular sensor.
|
||||
*/
|
||||
static void setMockValue(SensorType type, float value);
|
||||
|
||||
/*
|
||||
* Mock a value for a particular sensor.
|
||||
*/
|
||||
static void setMockValue(int type, float value);
|
||||
|
||||
/*
|
||||
* Reset mock for a particular sensor.
|
||||
*/
|
||||
static void resetMockValue(SensorType type);
|
||||
|
||||
/*
|
||||
* Reset mocking for all sensors.
|
||||
*/
|
||||
static void resetAllMocks();
|
||||
|
||||
protected:
|
||||
// Protected constructor - only subclasses call this
|
||||
explicit Sensor(SensorType type)
|
||||
: m_type(type) {}
|
||||
|
||||
private:
|
||||
// Retrieve the current reading from the sensor.
|
||||
//
|
||||
// Override this in a particular sensor's implementation. As reading sensors is in many hot paths,
|
||||
// it is unwise to synchronously read the sensor or do anything otherwise costly here. At the most,
|
||||
// this should be field lookup and simple math.
|
||||
virtual SensorResult get() const = 0;
|
||||
|
||||
SensorType m_type;
|
||||
|
||||
// Get this sensor's index in the list
|
||||
constexpr size_t getIndex() {
|
||||
return getIndex(m_type);
|
||||
}
|
||||
|
||||
// Get the index in the list for a sensor of particular type
|
||||
static constexpr size_t getIndex(SensorType type) {
|
||||
return static_cast<size_t>(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Static helper for sensor lookup
|
||||
*/
|
||||
static SensorRegistryEntry *getEntryForType(SensorType type);
|
||||
};
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* @file sensor_reader.h
|
||||
* @brief Declaration for SensorReader, the class used to acquire readings from a sensor.
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Provides an interface for reading sensors.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* // Create a sensor reader for type MySensor, and fallback value 10.0
|
||||
* SensorReader<SensorType::MySensor> mySensorReader(10.0f);
|
||||
*
|
||||
* <later>
|
||||
*
|
||||
* // Returns the sensor value if valid, or 10.0 if invalid.
|
||||
* float currentSensorValue = mySensorReader.getOrDefault();
|
||||
* alternatively, because we have implicit conversion:
|
||||
* float currentSensorValue = mySensorReader;
|
||||
*
|
||||
* Simply calling the get() method returns the full SensorResult, should
|
||||
* more complex logic be required in case of an invalid sensor reading.
|
||||
*/
|
||||
template <SensorType TSensorType>
|
||||
class SensorReader final {
|
||||
public:
|
||||
using sensorValueType = float;
|
||||
|
||||
explicit SensorReader(sensorValueType defaultValue)
|
||||
: m_defaultValue(defaultValue) {}
|
||||
|
||||
SensorResult get() const {
|
||||
return Sensor::get(TSensorType);
|
||||
}
|
||||
|
||||
// Get the sensor's reading, or a default value.
|
||||
// Inteded for applications where a default may be used silently,
|
||||
// while elsewhere in the world the failed sensor is otherwise handled.
|
||||
sensorValueType getOrDefault() const {
|
||||
auto result = get();
|
||||
|
||||
if (result.Valid) {
|
||||
return (sensorValueType)result.Value;
|
||||
} else {
|
||||
return m_defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Implicit conversion operator
|
||||
operator sensorValueType() const {
|
||||
return getOrDefault();
|
||||
}
|
||||
|
||||
private:
|
||||
const sensorValueType m_defaultValue;
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @file sensor_type.h
|
||||
* @brief Enumeration of sensors supported by the ECU.
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
**************************************
|
||||
* SEE sensor.h ON HOW TO ADD NEW SENSOR TYPES
|
||||
**************************************
|
||||
*/
|
||||
|
||||
enum class SensorType : unsigned char {
|
||||
Invalid = 0,
|
||||
Clt,
|
||||
Iat,
|
||||
|
||||
OilPressure,
|
||||
|
||||
// This is the "resolved" position, potentially composited out of the following two
|
||||
Tps1,
|
||||
// This is the first sensor
|
||||
Tps1Primary,
|
||||
// This is the second sensor
|
||||
Tps1Secondary,
|
||||
|
||||
// Leave me at the end!
|
||||
PlaceholderLast
|
||||
};
|
|
@ -10,4 +10,5 @@ CONTROLLERS_SENSORS_SRC_CPP = $(PROJECT_DIR)/controllers/sensors/thermistors.cp
|
|||
$(PROJECT_DIR)/controllers/sensors/ego.cpp \
|
||||
$(PROJECT_DIR)/controllers/sensors/maf2map.cpp \
|
||||
$(PROJECT_DIR)/controllers/sensors/hip9011_lookup.cpp \
|
||||
$(PROJECT_DIR)/controllers/sensors/oil_pressure.cpp
|
||||
$(PROJECT_DIR)/controllers/sensors/sensor.cpp \
|
||||
$(PROJECT_DIR)/controllers/sensors/linear_sensor.cpp
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @file stored_value_sensor.h
|
||||
* @brief Base class for a sensor that has its value asynchronously
|
||||
* set, then later retrieved by a consumer.
|
||||
*
|
||||
* @date September 12, 2019
|
||||
* @author Matthew Kennedy, (c) 2019
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
/**
|
||||
* @brief Base class for sensors that compute a value on one thread, and want
|
||||
* to make it available to consumers asynchronously.
|
||||
*
|
||||
* Common examples include sensors that have to do heavy lifting to produce
|
||||
* a reading, and don't want to perform that conversion at the time of
|
||||
* consumption.
|
||||
*
|
||||
* To use this class, create a class for your sensor that inherits StoredValueSensor,
|
||||
* and call Invalidate() and SetValidValue(float) as appropriate when readings are available
|
||||
* (or known to be invalid) for your sensor.
|
||||
*
|
||||
* Consumers will retrieve the last set (or invalidated) value.
|
||||
*/
|
||||
class StoredValueSensor : public Sensor {
|
||||
public:
|
||||
SensorResult get() const final {
|
||||
bool valid = m_isValid;
|
||||
float value = m_value;
|
||||
|
||||
return {valid, value};
|
||||
}
|
||||
|
||||
void setReportingLocation(float *reportLocation) {
|
||||
m_reportingLocation = reportLocation;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit StoredValueSensor(SensorType type)
|
||||
: Sensor(type)
|
||||
//, m_reportingLocation(reportingLocation)
|
||||
{}
|
||||
|
||||
// Invalidate the stored value.
|
||||
void invalidate() {
|
||||
m_isValid = false;
|
||||
}
|
||||
|
||||
// A new reading is available: set and validate a new value for the sensor.
|
||||
void setValidValue(float value) {
|
||||
// Set value before valid - so we don't briefly have the valid bit set on an invalid value
|
||||
m_value = value;
|
||||
m_isValid = true;
|
||||
|
||||
// Report value
|
||||
float *reportLoc = m_reportingLocation;
|
||||
if (reportLoc) {
|
||||
*reportLoc = value;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isValid = false;
|
||||
float m_value = 0.0f;
|
||||
|
||||
float *m_reportingLocation = nullptr;
|
||||
};
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "engine.h"
|
||||
#include "adc_inputs.h"
|
||||
#include "adc_subscription.h"
|
||||
#include "AdcConfiguration.h"
|
||||
#include "mpu_util.h"
|
||||
|
||||
|
@ -467,6 +468,8 @@ static void adc_callback_slow(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
|
|||
}
|
||||
slowAdcCounter++;
|
||||
}
|
||||
|
||||
AdcSubscription::UpdateSubscribers();
|
||||
}
|
||||
|
||||
static char errorMsgBuff[10];
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#include "adc_subscription.h"
|
||||
#include "adc_inputs.h"
|
||||
#include "engine.h"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
EXTERN_ENGINE;
|
||||
|
||||
#if !EFI_UNIT_TEST
|
||||
|
||||
struct AdcSubscriptionEntry {
|
||||
ConvertedSensor* Sensor;
|
||||
float VoltsPerAdcVolt;
|
||||
adc_channel_e Channel;
|
||||
};
|
||||
|
||||
static size_t s_nextEntry = 0;
|
||||
static AdcSubscriptionEntry s_entries[8];
|
||||
|
||||
void AdcSubscription::SubscribeSensor(ConvertedSensor& sensor, adc_channel_e channel, float voltsPerAdcVolt /*= 0.0f*/) {
|
||||
// Don't subscribe null channels
|
||||
if (channel == EFI_ADC_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// bounds check
|
||||
if (s_nextEntry >= std::size(s_entries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if 0, default to the board's divider coefficient
|
||||
if (voltsPerAdcVolt == 0) {
|
||||
voltsPerAdcVolt = engineConfiguration->analogInputDividerCoefficient;
|
||||
}
|
||||
|
||||
// Populate the entry
|
||||
auto& entry = s_entries[s_nextEntry];
|
||||
entry.Sensor = &sensor;
|
||||
entry.VoltsPerAdcVolt = voltsPerAdcVolt;
|
||||
entry.Channel = channel;
|
||||
|
||||
s_nextEntry++;
|
||||
}
|
||||
|
||||
void AdcSubscription::UpdateSubscribers() {
|
||||
for (size_t i = 0; i < s_nextEntry; i++) {
|
||||
auto& entry = s_entries[i];
|
||||
|
||||
float mcuVolts = getVoltage("sensor", entry.Channel);
|
||||
float sensorVolts = mcuVolts * entry.VoltsPerAdcVolt;
|
||||
|
||||
entry.Sensor->postRawValue(sensorVolts);
|
||||
}
|
||||
}
|
||||
|
||||
#endif// !EFI_UNIT_TEST
|
|
@ -0,0 +1,8 @@
|
|||
#include "converter_sensor.h"
|
||||
#include "rusefi_hw_enums.h"
|
||||
|
||||
class AdcSubscription {
|
||||
public:
|
||||
static void SubscribeSensor(ConvertedSensor& sensor, adc_channel_e channel, float voltsPerAdcVolt = 0.0f);
|
||||
static void UpdateSubscribers();
|
||||
};
|
|
@ -20,7 +20,8 @@ HW_LAYER_EMS_CPP = $(HW_LAYER_EGT_CPP) \
|
|||
$(PROJECT_DIR)/hw_layer/neo6m.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/mmc_card.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/lcd/lcd_HD44780.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/adc_inputs.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/adc_inputs.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/adc_subscription.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/pwm_generator.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/trigger_input.cpp \
|
||||
$(PROJECT_DIR)/hw_layer/hip9011.cpp \
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
void initSensors();
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
INIT_SRC_CPP = $(PROJECT_DIR)/init/sensor/init_sensors.cpp \
|
||||
$(PROJECT_DIR)/init/sensor/init_oil_pressure.cpp
|
|
@ -0,0 +1,42 @@
|
|||
#include "adc_subscription.h"
|
||||
#include "engine.h"
|
||||
#include "error_handling.h"
|
||||
#include "global.h"
|
||||
#include "linear_sensor.h"
|
||||
#include "tunerstudio_configuration.h"
|
||||
|
||||
EXTERN_ENGINE;
|
||||
|
||||
extern TunerStudioOutputChannels tsOutputChannels;
|
||||
|
||||
LinearSensor oilpSensor(SensorType::OilPressure);
|
||||
|
||||
void initOilPressure() {
|
||||
// Only register if we have a sensor
|
||||
auto channel = engineConfiguration->oilPressure.hwChannel;
|
||||
if (channel == EFI_ADC_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
oil_pressure_config_s *sensorCfg = &CONFIG(oilPressure);
|
||||
|
||||
float val1 = sensorCfg->value1;
|
||||
float val2 = sensorCfg->value2;
|
||||
|
||||
// Limit to max given pressure - val1 or val2 could be larger
|
||||
// (sensor may be backwards, high voltage = low pressure)
|
||||
float greaterOutput = val1 > val2 ? val1 : val2;
|
||||
|
||||
// Allow slightly negative output (-5kpa) so as to not fail the sensor when engine is off
|
||||
oilpSensor.configure(sensorCfg->v1, val1, sensorCfg->v2, val2, /*minOutput*/ -5, greaterOutput);
|
||||
|
||||
// Tell it to report to its output channel
|
||||
oilpSensor.setReportingLocation(&tsOutputChannels.oilPressure);
|
||||
|
||||
// Subscribe the sensor to the ADC
|
||||
AdcSubscription::SubscribeSensor(oilpSensor, channel);
|
||||
|
||||
if (!oilpSensor.Register()) {
|
||||
warning(OBD_Oil_Pressure_Sensor_Malfunction, "Duplicate oilp sensor registration, ignoring");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include "cli_registry.h"
|
||||
#include "init.h"
|
||||
#include "sensor.h"
|
||||
|
||||
void initOilPressure();
|
||||
|
||||
void initSensorCli();
|
||||
|
||||
void initSensors() {
|
||||
// aux sensors
|
||||
initOilPressure();
|
||||
|
||||
// Init CLI functionality for sensors (mocking)
|
||||
initSensorCli();
|
||||
}
|
||||
|
||||
void initSensorCli() {
|
||||
addConsoleActionIF("set_sensor_mock", Sensor::setMockValue);
|
||||
addConsoleAction("reset_sensor_mocks", Sensor::resetAllMocks);
|
||||
}
|
|
@ -97,6 +97,10 @@ void addConsoleActionIIP(const char *token, VoidIntIntVoidPtr callback, void *pa
|
|||
doAddAction(token, TWO_INTS_PARAMETER_P, (Void) callback, param);
|
||||
}
|
||||
|
||||
void addConsoleActionIF(const char *token, VoidIntFloat callback) {
|
||||
doAddAction(token, INT_FLOAT_PARAMETER, (Void) callback, NULL);
|
||||
}
|
||||
|
||||
void addConsoleActionS(const char *token, VoidCharPtr callback) {
|
||||
doAddAction(token, STRING_PARAMETER, (Void) callback, NULL);
|
||||
}
|
||||
|
@ -149,6 +153,7 @@ static int getParameterCount(action_type_e parameterType) {
|
|||
case STRING2_PARAMETER_P:
|
||||
case TWO_INTS_PARAMETER:
|
||||
case TWO_INTS_PARAMETER_P:
|
||||
case INT_FLOAT_PARAMETER:
|
||||
return 2;
|
||||
case STRING3_PARAMETER:
|
||||
return 3;
|
||||
|
@ -394,7 +399,7 @@ void handleActionWithParameter(TokenCallback *current, char *parameter) {
|
|||
print("invalid float [%s]\r\n", parameter);
|
||||
return;
|
||||
}
|
||||
if (current->parameterType == FLOAT_FLOAT_PARAMETER) {
|
||||
if (current->parameterType == FLOAT_FLOAT_PARAMETER) {
|
||||
VoidFloatFloat callbackS = (VoidFloatFloat) current->callback;
|
||||
(*callbackS)(value1, value2);
|
||||
} else {
|
||||
|
@ -404,6 +409,32 @@ if (current->parameterType == FLOAT_FLOAT_PARAMETER) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (current->parameterType == INT_FLOAT_PARAMETER) {
|
||||
int spaceIndex = findEndOfToken(parameter);
|
||||
if (spaceIndex == -1)
|
||||
return;
|
||||
REPLACE_SPACES_WITH_ZERO;
|
||||
int value1 = atoi(parameter);
|
||||
if (absI(value1) == ERROR_CODE) {
|
||||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
||||
scheduleMsg(logging, "not an integer [%s]", parameter);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
parameter += spaceIndex + 1;
|
||||
float value2 = atoff(parameter);
|
||||
|
||||
if (cisnan(value2)) {
|
||||
print("invalid float [%s]\r\n", parameter);
|
||||
return;
|
||||
}
|
||||
|
||||
VoidIntFloat callback = (VoidIntFloat) current->callback;
|
||||
callback(value1, value2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int value = atoi(parameter);
|
||||
if (absI(value) == ERROR_CODE) {
|
||||
print("invalid integer [%s]\r\n", parameter);
|
||||
|
|
|
@ -30,6 +30,7 @@ typedef enum {
|
|||
TWO_INTS_PARAMETER_P,
|
||||
FLOAT_FLOAT_PARAMETER,
|
||||
FLOAT_FLOAT_PARAMETER_P,
|
||||
INT_FLOAT_PARAMETER,
|
||||
} action_type_e;
|
||||
|
||||
typedef struct {
|
||||
|
@ -61,6 +62,8 @@ void addConsoleActionP(const char *token, VoidPtr callback, void *param);
|
|||
void addConsoleActionI(const char *token, VoidInt callback);
|
||||
void addConsoleActionIP(const char *token, VoidIntVoidPtr callback, void *param);
|
||||
|
||||
void addConsoleActionIF(const char* token, VoidIntFloat callback);
|
||||
|
||||
void addConsoleActionII(const char *token, VoidIntInt callback);
|
||||
void addConsoleActionIIP(const char *token, VoidIntIntVoidPtr callback, void *param);
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
#include "mock/mock_sensor.h"
|
||||
#include "stored_value_sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorBasic : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SensorBasic, Registry) {
|
||||
// Create a sensor - but don't register it
|
||||
MockSensor dut(SensorType::Tps1);
|
||||
|
||||
// Expect not to find it
|
||||
{
|
||||
auto s = Sensor::getSensorOfType(SensorType::Tps1);
|
||||
EXPECT_FALSE(s);
|
||||
}
|
||||
|
||||
// Register the sensor now
|
||||
EXPECT_TRUE(dut.Register());
|
||||
|
||||
// It should now get us back our sensor
|
||||
{
|
||||
auto s = Sensor::getSensorOfType(SensorType::Tps1);
|
||||
EXPECT_EQ(s, &dut);
|
||||
}
|
||||
|
||||
// Reset - it should now be gone!
|
||||
Sensor::resetRegistry();
|
||||
|
||||
{
|
||||
auto s = Sensor::getSensorOfType(SensorType::Tps1);
|
||||
EXPECT_FALSE(s);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorBasic, DoubleRegister) {
|
||||
// Create a sensor, register it
|
||||
MockSensor dut(SensorType::Tps1);
|
||||
ASSERT_TRUE(dut.Register());
|
||||
|
||||
// And then do it again!
|
||||
MockSensor dut2(SensorType::Tps1);
|
||||
EXPECT_FALSE(dut2.Register());
|
||||
|
||||
// Make sure that we get the first DUT back - not the second
|
||||
auto shouldBeDut = Sensor::getSensorOfType(SensorType::Tps1);
|
||||
EXPECT_EQ(shouldBeDut, &dut);
|
||||
EXPECT_NE(shouldBeDut, &dut2);
|
||||
}
|
||||
|
||||
TEST_F(SensorBasic, SensorNotInitialized) {
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
|
||||
EXPECT_FALSE(result.Valid);
|
||||
}
|
||||
|
||||
TEST_F(SensorBasic, SensorInitialized) {
|
||||
MockSensor dut(SensorType::Clt);
|
||||
ASSERT_TRUE(dut.Register());
|
||||
|
||||
// Check before init - make sure it isn't set yet
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
ASSERT_FALSE(result.Valid);
|
||||
|
||||
// Set a value
|
||||
dut.set(75);
|
||||
|
||||
// Make sure now it's set
|
||||
auto result2 = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result2.Valid);
|
||||
EXPECT_FLOAT_EQ(result2.Value, 75);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#include "converter_sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorConverted : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
};
|
||||
|
||||
class DoublerConverterSensor final : public ConvertedSensor {
|
||||
public:
|
||||
DoublerConverterSensor()
|
||||
: ConvertedSensor(SensorType::Clt) {}
|
||||
|
||||
protected:
|
||||
SensorResult convertFromInputValue(float input) {
|
||||
bool valid = input > 0;
|
||||
float value = input * 2;
|
||||
|
||||
return {valid, value};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SensorConverted, TestValid) {
|
||||
DoublerConverterSensor dut;
|
||||
ASSERT_TRUE(dut.Register());
|
||||
|
||||
// Should be invalid - not set yet
|
||||
{
|
||||
auto s = Sensor::get(SensorType::Clt);
|
||||
EXPECT_FALSE(s.Valid);
|
||||
}
|
||||
|
||||
dut.postRawValue(25);
|
||||
|
||||
// Should be valid, with a value of 25*2 = 50
|
||||
{
|
||||
auto s = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(s.Valid);
|
||||
EXPECT_FLOAT_EQ(s.Value, 50);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorConverted, TestInvalid) {
|
||||
DoublerConverterSensor dut;
|
||||
ASSERT_TRUE(dut.Register());
|
||||
|
||||
// Should be invalid - not set yet
|
||||
{
|
||||
auto s = Sensor::get(SensorType::Clt);
|
||||
EXPECT_FALSE(s.Valid);
|
||||
}
|
||||
|
||||
dut.postRawValue(-25);
|
||||
|
||||
// Should be invalid, with a value of -25*2 = 0
|
||||
{
|
||||
auto s = Sensor::get(SensorType::Clt);
|
||||
EXPECT_FALSE(s.Valid);
|
||||
EXPECT_FLOAT_EQ(s.Value, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#include "function_pointer_sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorFunctionPointer : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
};
|
||||
|
||||
float testFunc() {
|
||||
return 23;
|
||||
}
|
||||
|
||||
TEST_F(SensorFunctionPointer, TestValue) {
|
||||
FunctionPointerSensor dut(SensorType::Clt, testFunc);
|
||||
ASSERT_TRUE(dut.Register());
|
||||
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 23);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
#include "linear_sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorLinear : public ::testing::Test {
|
||||
protected:
|
||||
// Maps (1, 4) -> (100, -100)
|
||||
LinearSensor dut;
|
||||
|
||||
SensorLinear()
|
||||
: dut(SensorType::Clt) {}
|
||||
|
||||
void SetUp() override {
|
||||
dut.configure(1, 100, 4, -100, -110, 110);
|
||||
}
|
||||
};
|
||||
|
||||
#define test_point(in, out) \
|
||||
{ \
|
||||
dut.postRawValue(in); \
|
||||
auto result = dut.get(); \
|
||||
\
|
||||
EXPECT_TRUE(result.Valid); \
|
||||
EXPECT_FLOAT_EQ(result.Value, (out)); \
|
||||
}
|
||||
|
||||
#define test_point_invalid(in) \
|
||||
{ \
|
||||
dut.postRawValue(in); \
|
||||
EXPECT_FALSE(dut.get().Valid); \
|
||||
}
|
||||
|
||||
TEST_F(SensorLinear, TestInRange) {
|
||||
test_point(2.5, 0);
|
||||
test_point(1, 100);
|
||||
test_point(4, -100);
|
||||
}
|
||||
|
||||
TEST_F(SensorLinear, TestOutOfRange) {
|
||||
test_point(1, 100);
|
||||
test_point_invalid(0.5);
|
||||
|
||||
test_point(4, -100);
|
||||
test_point_invalid(4.5);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "stored_value_sensor.h"
|
||||
|
||||
struct MockSensor final : public StoredValueSensor
|
||||
{
|
||||
MockSensor(SensorType type) : StoredValueSensor(type)
|
||||
{
|
||||
}
|
||||
|
||||
void set(float value)
|
||||
{
|
||||
setValidValue(value);
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
StoredValueSensor::invalidate();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
#include "mock/mock_sensor.h"
|
||||
|
||||
#include "sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorMocking : public ::testing::Test {
|
||||
protected:
|
||||
MockSensor realSensor;
|
||||
|
||||
SensorMocking()
|
||||
: realSensor(SensorType::Clt) {}
|
||||
|
||||
void SetUp() override {
|
||||
Sensor::resetRegistry();
|
||||
ASSERT_TRUE(realSensor.Register());
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SensorMocking, MockSensor) {
|
||||
// Set a value on the "real" sensor
|
||||
realSensor.set(25.0f);
|
||||
|
||||
// And expect to see it
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 25.0f);
|
||||
}
|
||||
|
||||
// Now set a mock value
|
||||
Sensor::setMockValue(SensorType::Clt, 37.0f);
|
||||
|
||||
// And expect to see that
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 37.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorMocking, ResetOne) {
|
||||
// Set a value on the "real" sensor
|
||||
realSensor.set(13.0f);
|
||||
|
||||
// Now set a mock value
|
||||
Sensor::setMockValue(SensorType::Clt, 21.0f);
|
||||
|
||||
// Expect to see the mock value
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 21.0f);
|
||||
}
|
||||
|
||||
// Reset that mock - it should go to the real value
|
||||
Sensor::resetMockValue(SensorType::Clt);
|
||||
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 13.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorMocking, ResetAll) {
|
||||
// Set a value on the "real" sensor
|
||||
realSensor.set(46.0f);
|
||||
|
||||
// Now set a mock value
|
||||
Sensor::setMockValue(SensorType::Clt, 33.0f);
|
||||
|
||||
// Expect to see the mock value
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 33.0f);
|
||||
}
|
||||
|
||||
// Reset all mocks - it should go to the real value
|
||||
Sensor::resetAllMocks();
|
||||
|
||||
{
|
||||
auto result = Sensor::get(SensorType::Clt);
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 46.0f);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#include "sensor_reader.h"
|
||||
|
||||
#include "mock/mock_sensor.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class SensorBasicReader : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Sensor::resetRegistry();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SensorBasicReader, Value) {
|
||||
// Create a sensor - but don't register it
|
||||
MockSensor dut(SensorType::Tps1);
|
||||
|
||||
// Create a reader for the same sensor type - fallback value = 123
|
||||
SensorReader<SensorType::Tps1> dutReader(123.0f);
|
||||
|
||||
// Expect not to find it
|
||||
{
|
||||
auto result = dutReader.get();
|
||||
EXPECT_FALSE(result.Valid);
|
||||
}
|
||||
|
||||
// Register the sensor now
|
||||
EXPECT_TRUE(dut.Register());
|
||||
|
||||
// Still expect invalid - no value has been set yet
|
||||
{
|
||||
auto result = dutReader.get();
|
||||
EXPECT_FALSE(result.Valid);
|
||||
}
|
||||
|
||||
dut.set(47.0f);
|
||||
|
||||
// Expect valid - with the value 47 we just set
|
||||
{
|
||||
auto result = dutReader.get();
|
||||
EXPECT_TRUE(result.Valid);
|
||||
EXPECT_FLOAT_EQ(result.Value, 47.0f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SensorBasicReader, FallbackValue) {
|
||||
// Create a sensor - but don't register it
|
||||
MockSensor dut(SensorType::Tps1);
|
||||
|
||||
// Create a reader for the same sensor type - fallback value = 123
|
||||
SensorReader<SensorType::Tps1> dutReader(123.0f);
|
||||
|
||||
// Expect not to find it
|
||||
{
|
||||
auto result = dutReader.get();
|
||||
EXPECT_FALSE(result.Valid);
|
||||
}
|
||||
|
||||
// Register the sensor now
|
||||
EXPECT_TRUE(dut.Register());
|
||||
|
||||
// Still expect invalid - no value has been set yet
|
||||
{
|
||||
auto result = dutReader.get();
|
||||
EXPECT_FALSE(result.Valid);
|
||||
}
|
||||
|
||||
// However - we should be able to get the fallback value
|
||||
{
|
||||
auto result = dutReader.getOrDefault();
|
||||
EXPECT_FLOAT_EQ(result, 123.0f);
|
||||
}
|
||||
|
||||
// Now set the value for real, and ensure we get that instead
|
||||
dut.set(63);
|
||||
|
||||
{
|
||||
auto result = dutReader.getOrDefault();
|
||||
EXPECT_FLOAT_EQ(result, 63.0f);
|
||||
}
|
||||
}
|
|
@ -26,4 +26,11 @@ TESTS_SRC_CPP = \
|
|||
tests/test_sensors.cpp \
|
||||
tests/test_pid_auto.cpp \
|
||||
tests/test_accel_enrichment.cpp \
|
||||
tests/test_gpiochip.cpp
|
||||
tests/test_gpiochip.cpp \
|
||||
\
|
||||
tests/sensor/basic_sensor.cpp \
|
||||
tests/sensor/converter_sensor.cpp \
|
||||
tests/sensor/function_pointer_sensor.cpp \
|
||||
tests/sensor/mock_sensor.cpp \
|
||||
tests/sensor/sensor_reader.cpp \
|
||||
tests/sensor/lin_sensor.cpp
|
||||
|
|
Loading…
Reference in New Issue