FSIO refactoring (#1861)

* better fsio tests

* condense operators

* minor parser cleanup too

* comments, too!
This commit is contained in:
Matthew Kennedy 2020-10-06 18:54:49 -07:00 committed by GitHub
parent 3ded888df3
commit 8cff16e797
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 118 deletions

View File

@ -86,6 +86,11 @@ void LEElement::init(le_action_e action, float fValue) {
this->fValue = fValue;
}
void LEElement::init(le_action_e action, bool bValue) {
this->action = action;
this->fValue = bValue ? 1 : 0;
}
LECalculator::LECalculator() {
reset();
}
@ -135,6 +140,48 @@ void LECalculator::push(le_action_e action, float value) {
}
}
static float doBinaryBoolean(le_action_e action, float lhs, float rhs) {
bool v1 = float2bool(lhs);
bool v2 = float2bool(rhs);
switch (action) {
case LE_OPERATOR_AND:
return v1 && v2;
case LE_OPERATOR_OR:
return v1 || v2;
default:
return NAN;
}
}
static float doBinaryNumeric(le_action_e action, float v1, float v2) {
// Process based on the action type
switch (action) {
case LE_OPERATOR_ADDITION:
return v1 + v2;
case LE_OPERATOR_SUBTRACTION:
return v1 - v2;
case LE_OPERATOR_MULTIPLICATION:
return v1 * v2;
case LE_OPERATOR_DIVISION:
return v1 / v2;
case LE_OPERATOR_LESS:
return v1 < v2;
case LE_OPERATOR_MORE:
return v1 > v2;
case LE_OPERATOR_LESS_OR_EQUAL:
return v1 <= v2;
case LE_OPERATOR_MORE_OR_EQUAL:
return v1 >= v2;
case LE_METHOD_MIN:
return minF(v1, v2);
case LE_METHOD_MAX:
return maxF(v1, v2);
default:
return NAN;
}
}
/**
* @return true in case of error, false otherwise
*/
@ -143,93 +190,50 @@ bool LECalculator::processElement(LEElement *element DECLARE_ENGINE_PARAMETER_SU
efiAssert(CUSTOM_ERR_ASSERT, getCurrentRemainingStack() > 64, "FSIO logic", false);
#endif
switch (element->action) {
// Literal values
case LE_NUMERIC_VALUE:
push(element->action, element->fValue);
break;
case LE_OPERATOR_AND: {
float v1 = pop(LE_OPERATOR_AND);
float v2 = pop(LE_OPERATOR_AND);
push(element->action, float2bool(v1) && float2bool(v2));
}
case LE_BOOLEAN_VALUE:
push(element->action, element->fValue != 0);
break;
// Boolean input binary operators
case LE_OPERATOR_AND:
case LE_OPERATOR_OR: {
float v1 = pop(LE_OPERATOR_OR);
float v2 = pop(LE_OPERATOR_OR);
push(element->action, float2bool(v1) || float2bool(v2));
}
break;
case LE_OPERATOR_LESS: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_LESS);
float v1 = pop(LE_OPERATOR_LESS);
auto result = doBinaryBoolean(element->action, v1, v2);
push(element->action, v1 < v2);
push(element->action, result);
}
break;
// Numeric input binary operators
case LE_OPERATOR_ADDITION:
case LE_OPERATOR_SUBTRACTION:
case LE_OPERATOR_MULTIPLICATION:
case LE_OPERATOR_DIVISION:
case LE_OPERATOR_LESS:
case LE_OPERATOR_MORE:
case LE_OPERATOR_LESS_OR_EQUAL:
case LE_OPERATOR_MORE_OR_EQUAL:
case LE_METHOD_MIN:
case LE_METHOD_MAX: {
// elements on stack are in reverse order
float v2 = pop(element->action);
float v1 = pop(element->action);
auto result = doBinaryNumeric(element->action, v1, v2);
push(element->action, result);
}
break;
// Boolean input unary operator
case LE_OPERATOR_NOT: {
float v = pop(LE_OPERATOR_NOT);
push(element->action, !float2bool(v));
}
break;
case LE_OPERATOR_MORE: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE);
float v1 = pop(LE_OPERATOR_MORE);
push(element->action, v1 > v2);
}
break;
case LE_OPERATOR_ADDITION: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE);
float v1 = pop(LE_OPERATOR_MORE);
push(element->action, v1 + v2);
}
break;
case LE_OPERATOR_SUBTRACTION: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE);
float v1 = pop(LE_OPERATOR_MORE);
push(element->action, v1 - v2);
}
break;
case LE_OPERATOR_MULTIPLICATION: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE);
float v1 = pop(LE_OPERATOR_MORE);
push(element->action, v1 * v2);
}
break;
case LE_OPERATOR_DIVISION: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE);
float v1 = pop(LE_OPERATOR_MORE);
push(element->action, v1 / v2);
}
break;
case LE_OPERATOR_LESS_OR_EQUAL: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_LESS_OR_EQUAL);
float v1 = pop(LE_OPERATOR_LESS_OR_EQUAL);
push(element->action, v1 <= v2);
}
break;
case LE_OPERATOR_MORE_OR_EQUAL: {
// elements on stack are in reverse order
float v2 = pop(LE_OPERATOR_MORE_OR_EQUAL);
float v1 = pop(LE_OPERATOR_MORE_OR_EQUAL);
push(element->action, v1 >= v2);
}
break;
case LE_METHOD_IF: {
// elements on stack are in reverse order
float vFalse = pop(LE_METHOD_IF);
@ -238,18 +242,6 @@ bool LECalculator::processElement(LEElement *element DECLARE_ENGINE_PARAMETER_SU
push(element->action, vCond != 0 ? vTrue : vFalse);
}
break;
case LE_METHOD_MAX: {
float v2 = pop(LE_METHOD_MAX);
float v1 = pop(LE_METHOD_MAX);
push(element->action, maxF(v1, v2));
}
break;
case LE_METHOD_MIN: {
float v2 = pop(LE_METHOD_MIN);
float v1 = pop(LE_METHOD_MIN);
push(element->action, minF(v1, v2));
}
break;
case LE_METHOD_FSIO_SETTING: {
float humanIndex = pop(LE_METHOD_FSIO_SETTING);
int index = (int) humanIndex - 1;
@ -282,6 +274,7 @@ bool LECalculator::processElement(LEElement *element DECLARE_ENGINE_PARAMETER_SU
break;
case LE_METHOD_FSIO_DIGITAL_INPUT:
// todo: implement code for digital input!!!
return true;
case LE_METHOD_FSIO_ANALOG_INPUT:
{
int index = clampF(0, pop(LE_METHOD_FSIO_ANALOG_INPUT), FSIO_ANALOG_INPUT_COUNT - 1);
@ -370,6 +363,13 @@ bool isNumeric(const char* line) {
return line[0] >= '0' && line[0] <= '9';
}
bool isBoolean(const char* line) {
bool isTrue = 0 == strcmp(line, "true");
bool isFalse = 0 == strcmp(line, "false");
return isTrue || isFalse;
}
/**
* @return pointer at the position after the consumed token, null if no token consumed
*/
@ -406,27 +406,28 @@ le_action_e parseAction(const char * line) {
static char parsingBuffer[64];
LEElement *LEElementPool::parseExpression(const char * line) {
LEElement *first = nullptr;
LEElement *last = nullptr;
while (true) {
line = getNextToken(line, parsingBuffer, sizeof(parsingBuffer));
if (line == nullptr) {
if (!line) {
/**
* No more tokens in this line
* No more tokens in this line, parsing complete!
*/
return first;
}
LEElement *n = next();
if (n == nullptr) {
return first;
if (!n) {
return nullptr;
}
if (isNumeric(parsingBuffer)) {
n->init(LE_NUMERIC_VALUE, atoff(parsingBuffer));
} else if (isBoolean(parsingBuffer)) {
n->init(LE_BOOLEAN_VALUE, parsingBuffer[0] == 't');
} else {
le_action_e action = parseAction(parsingBuffer);
if (action == LE_UNDEFINED) {
@ -439,15 +440,18 @@ LEElement *LEElementPool::parseExpression(const char * line) {
n->init(action);
}
if (first == nullptr) {
// If this is the first time through, set the first element.
if (!first) {
first = n;
last = n;
} else {
last->next = n;
last = last->next;
}
// If not the first, link the list
if (last) {
last->next = n;
}
last = n;
}
return first;
}
#endif /* EFI_FSIO */

View File

@ -16,6 +16,7 @@ typedef enum {
LE_UNDEFINED = 0 ,
LE_NUMERIC_VALUE = 1,
LE_BOOLEAN_VALUE = 126,
LE_OPERATOR_LESS = 2,
LE_OPERATOR_MORE = 3,
LE_OPERATOR_LESS_OR_EQUAL = 4,
@ -66,9 +67,10 @@ class LEElement {
public:
LEElement();
void clear();
// void init(le_action_e action, int iValue);
void init(le_action_e action);
void init(le_action_e action, float fValue);
void init(le_action_e action, float value);
void init(le_action_e action, bool value);
le_action_e action;
float fValue;

View File

@ -40,7 +40,7 @@ float getEngineValue(le_action_e action DECLARE_ENGINE_PARAMETER_SUFFIX) {
}
}
static void testParsing(void) {
TEST(fsio, testParsing) {
char buffer[64];
ASSERT_TRUE(strEqualCaseInsensitive("hello", "HELlo"));
@ -95,7 +95,7 @@ static void testExpression2(float selfValue, const char *line, float expected, E
EXPAND_Engine;
ASSERT_EQ(expected, c.getValue2(selfValue, element PASS_ENGINE_PARAMETER_SUFFIX)) << line;
ASSERT_NEAR(expected, c.getValue2(selfValue, element PASS_ENGINE_PARAMETER_SUFFIX), EPS4D) << line;
}
static void testExpression2(float selfValue, const char *line, float expected, const std::unordered_map<SensorType, float>& sensorVals = {}) {
@ -107,10 +107,6 @@ static void testExpression(const char *line, float expectedValue, const std::uno
testExpression2(0, line, expectedValue, sensorVals);
}
TEST(fsio, testIfFunction) {
testExpression("1 22 33 if", 22);
}
TEST(fsio, testHysteresisSelf) {
WITH_ENGINE_TEST_HELPER(FORD_INLINE_6_1995);
@ -144,8 +140,58 @@ TEST(fsio, testHysteresisSelf) {
ASSERT_EQ(1, selfValue);
}
TEST(fsio, testLiterals) {
// Constants - single token
testExpression("123", 123.0f);
testExpression("true", 1);
testExpression("false", 0);
}
TEST(fsio, mathOperators) {
// Test basic operations
testExpression("123 456 +", 579);
testExpression("123 456 -", -333);
testExpression("123 456 *", 56088);
testExpression("123 456 /", 0.269737f);
}
TEST(fsio, comparisonOperators) {
// Comparison operators
testExpression("123 456 >", 0);
testExpression("123 456 >=", 0);
testExpression("123 456 <", 1);
testExpression("123 456 <=", 1);
testExpression("123 456 min", 123);
testExpression("123 456 max", 456);
}
TEST(fsio, booleanOperators) {
// Boolean operators
testExpression("true true and", 1);
testExpression("true false and", 0);
testExpression("true false or", 1);
testExpression("false false or", 0);
// (both ways to write and/or)
testExpression("true true &", 1);
testExpression("true false &", 0);
testExpression("true false |", 1);
testExpression("false false |", 0);
// not operator
testExpression("true not", 0);
testExpression("false not", 1);
}
TEST(fsio, extraOperators) {
// Self operator
testExpression2(123, "self 1 +", 124);
// ternary operator
testExpression("1 22 33 if", 22);
testExpression("0 22 33 if", 33);
}
TEST(fsio, testLogicExpressions) {
testParsing();
{
WITH_ENGINE_TEST_HELPER(FORD_INLINE_6_1995);
@ -153,13 +199,13 @@ TEST(fsio, testLogicExpressions) {
LECalculator c;
LEElement value1;
value1.init(LE_NUMERIC_VALUE, 123.0);
value1.init(LE_NUMERIC_VALUE, 123.0f);
c.add(&value1);
assertEqualsM("123", 123.0, c.getValue(0 PASS_ENGINE_PARAMETER_SUFFIX));
LEElement value2;
value2.init(LE_NUMERIC_VALUE, 321.0);
value2.init(LE_NUMERIC_VALUE, 321.0f);
c.add(&value2);
LEElement value3;
@ -180,7 +226,7 @@ TEST(fsio, testLogicExpressions) {
e->init(LE_METHOD_TIME_SINCE_BOOT);
e = pool.next();
e->init(LE_NUMERIC_VALUE, 4);
e->init(LE_NUMERIC_VALUE, 4.0f);
e = pool.next();
e->init(LE_OPERATOR_LESS);
@ -189,7 +235,7 @@ TEST(fsio, testLogicExpressions) {
e->init(LE_METHOD_RPM);
e = pool.next();
e->init(LE_NUMERIC_VALUE, 0);
e->init(LE_NUMERIC_VALUE, 0.0f);
e = pool.next();
e->init(LE_OPERATOR_MORE);
@ -217,11 +263,6 @@ TEST(fsio, testLogicExpressions) {
testExpression("coolant 90 >", 1, sensorVals);
testExpression("fan not coolant 90 > and", 1, sensorVals);
testExpression("100 200 1 if", 200);
testExpression("10 99 max", 99);
testExpression2(123, "10 self max", 123);
testExpression("fan NOT coolant 90 > AND fan coolant 85 > AND OR", 1, sensorVals);
{
@ -238,12 +279,6 @@ TEST(fsio, testLogicExpressions) {
ASSERT_EQ(0, c.calcLogValue[0]);
}
testExpression("0 1 &", 0);
testExpression("0 1 |", 1);
testExpression("0 1 >", 0);
{
WITH_ENGINE_TEST_HELPER_SENS(FORD_INLINE_6_1995, sensorVals);
engineConfiguration->fanOnTemperature = 0;