rework FSIO LEelement (#2374)
* save * firmware * test parsing multiple things * split tests
This commit is contained in:
parent
d01db86384
commit
102804db7d
|
@ -73,7 +73,6 @@ LEElement::LEElement() {
|
|||
|
||||
void LEElement::clear() {
|
||||
action = LE_UNDEFINED;
|
||||
next = nullptr;
|
||||
fValue = NAN;
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,7 @@ LECalculator::LECalculator() {
|
|||
}
|
||||
|
||||
void LECalculator::reset() {
|
||||
first = nullptr;
|
||||
m_program = nullptr;
|
||||
stack.reset();
|
||||
currentCalculationLogPosition = 0;
|
||||
memset(calcLogAction, 0, sizeof(calcLogAction));
|
||||
|
@ -104,19 +103,11 @@ void LECalculator::reset() {
|
|||
|
||||
void LECalculator::reset(LEElement *element) {
|
||||
reset();
|
||||
add(element);
|
||||
setProgram(element);
|
||||
}
|
||||
|
||||
void LECalculator::add(LEElement *element) {
|
||||
if (first == nullptr) {
|
||||
first = element;
|
||||
} else {
|
||||
LEElement *last = first;
|
||||
while (last->next != NULL) {
|
||||
last = last->next;
|
||||
}
|
||||
last->next = element;
|
||||
}
|
||||
void LECalculator::setProgram(LEElement* program) {
|
||||
m_program = program;
|
||||
}
|
||||
|
||||
bool float2bool(float v) {
|
||||
|
@ -185,7 +176,7 @@ static FsioResult doBinaryNumeric(le_action_e action, float v1, float v2) {
|
|||
/**
|
||||
* @return true in case of error, false otherwise
|
||||
*/
|
||||
FsioResult LECalculator::processElement(LEElement *element DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
FsioResult LECalculator::processElement(const LEElement *element DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
#if EFI_PROD_CODE
|
||||
efiAssert(CUSTOM_ERR_ASSERT, getCurrentRemainingStack() > 64, "FSIO logic", unexpected);
|
||||
#endif
|
||||
|
@ -284,7 +275,7 @@ float LECalculator::getValue2(float selfValue, LEElement *fistElementInList DECL
|
|||
}
|
||||
|
||||
bool LECalculator::isEmpty() const {
|
||||
return first == NULL;
|
||||
return !m_program;
|
||||
}
|
||||
|
||||
float LECalculator::getValue(float selfValue DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
|
@ -292,12 +283,15 @@ float LECalculator::getValue(float selfValue DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
warning(CUSTOM_NO_FSIO, "no FSIO code");
|
||||
return NAN;
|
||||
}
|
||||
LEElement *element = first;
|
||||
|
||||
const LEElement* element = m_program;
|
||||
|
||||
stack.reset();
|
||||
|
||||
int counter = 0;
|
||||
while (element != NULL) {
|
||||
|
||||
// while not a return statement, execute instructions
|
||||
while (element->action != LE_METHOD_RETURN) {
|
||||
efiAssert(CUSTOM_ERR_ASSERT, counter < 200, "FSIOcount", NAN); // just in case
|
||||
|
||||
if (element->action == LE_METHOD_SELF) {
|
||||
|
@ -312,39 +306,33 @@ float LECalculator::getValue(float selfValue DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
|
||||
push(element->action, result.Value);
|
||||
}
|
||||
element = element->next;
|
||||
|
||||
// Step forward to the next instruction in sequence
|
||||
element++;
|
||||
counter++;
|
||||
}
|
||||
|
||||
// The stack should have exactly one element on it
|
||||
if (stack.size() != 1) {
|
||||
warning(CUSTOM_FSIO_STACK_SIZE, "unexpected FSIO stack size: %d", stack.size());
|
||||
warning(CUSTOM_FSIO_STACK_SIZE, "unexpected FSIO stack size at return: %d", stack.size());
|
||||
return NAN;
|
||||
}
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
LEElementPool::LEElementPool(LEElement *pool, int size) {
|
||||
this->pool = pool;
|
||||
this->m_pool = pool;
|
||||
this->size = size;
|
||||
reset();
|
||||
}
|
||||
|
||||
void LEElementPool::reset() {
|
||||
index = 0;
|
||||
// Next free element is the first one
|
||||
m_nextFree = m_pool;
|
||||
}
|
||||
|
||||
int LEElementPool::getSize() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
LEElement *LEElementPool::next() {
|
||||
if (index >= size) {
|
||||
// todo: this should not be a fatal error, just an error
|
||||
firmwareError(CUSTOM_ERR_FSIO_POOL, "LE_ELEMENT_POOL_SIZE overflow");
|
||||
return NULL;
|
||||
}
|
||||
LEElement *result = &pool[index++];
|
||||
result->clear();
|
||||
return result;
|
||||
return m_nextFree - m_pool;
|
||||
}
|
||||
|
||||
bool isNumeric(const char* line) {
|
||||
|
@ -393,9 +381,9 @@ le_action_e parseAction(const char * line) {
|
|||
|
||||
static char parsingBuffer[64];
|
||||
|
||||
LEElement *LEElementPool::parseExpression(const char * line) {
|
||||
LEElement *first = nullptr;
|
||||
LEElement *last = nullptr;
|
||||
LEElement* LEElementPool::parseExpression(const char * line) {
|
||||
LEElement* expressionHead = m_nextFree;
|
||||
LEElement* n = expressionHead;
|
||||
|
||||
while (true) {
|
||||
line = getNextToken(line, parsingBuffer, sizeof(parsingBuffer));
|
||||
|
@ -404,12 +392,13 @@ LEElement *LEElementPool::parseExpression(const char * line) {
|
|||
/**
|
||||
* No more tokens in this line, parsing complete!
|
||||
*/
|
||||
return first;
|
||||
}
|
||||
|
||||
LEElement *n = next();
|
||||
if (!n) {
|
||||
return nullptr;
|
||||
// Push a return statement on the end
|
||||
n->init(LE_METHOD_RETURN);
|
||||
|
||||
// The next available element is the one after the return
|
||||
m_nextFree = n + 1;
|
||||
return expressionHead;
|
||||
}
|
||||
|
||||
if (isNumeric(parsingBuffer)) {
|
||||
|
@ -429,17 +418,7 @@ LEElement *LEElementPool::parseExpression(const char * line) {
|
|||
n->init(action);
|
||||
}
|
||||
|
||||
// If this is the first time through, set the first element.
|
||||
if (!first) {
|
||||
first = n;
|
||||
}
|
||||
|
||||
// If not the first, link the list
|
||||
if (last) {
|
||||
last->next = n;
|
||||
}
|
||||
|
||||
last = n;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef enum {
|
|||
LE_METHOD_PPS = 125,
|
||||
LE_METHOD_TIME_SINCE_TRIGGER_EVENT = 127,
|
||||
LE_METHOD_IN_MR_BENCH = 128,
|
||||
LE_METHOD_RETURN = 130,
|
||||
|
||||
#include "fsio_enums_generated.def"
|
||||
|
||||
|
@ -103,20 +104,19 @@ public:
|
|||
|
||||
le_action_e action;
|
||||
float fValue;
|
||||
|
||||
LEElement *next;
|
||||
};
|
||||
|
||||
class LEElementPool {
|
||||
public:
|
||||
LEElementPool(LEElement *pool, int size);
|
||||
LEElement *pool;
|
||||
LEElement *next();
|
||||
|
||||
void reset();
|
||||
LEElement * parseExpression(const char * line);
|
||||
int getSize() const;
|
||||
private:
|
||||
int index;
|
||||
LEElement* m_pool;
|
||||
LEElement* m_nextFree;
|
||||
|
||||
int size;
|
||||
};
|
||||
|
||||
|
@ -132,18 +132,25 @@ public:
|
|||
LECalculator();
|
||||
float getValue(float selfValue DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
float getValue2(float selfValue, LEElement *fistElementInList DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
void add(LEElement *element);
|
||||
|
||||
bool isEmpty() const;
|
||||
void reset();
|
||||
void reset(LEElement *element);
|
||||
|
||||
// Log history of calculation actions for debugging
|
||||
le_action_e calcLogAction[MAX_CALC_LOG];
|
||||
float calcLogValue[MAX_CALC_LOG];
|
||||
int currentCalculationLogPosition;
|
||||
|
||||
private:
|
||||
void setProgram(LEElement* program);
|
||||
|
||||
void push(le_action_e action, float value);
|
||||
FsioResult processElement(LEElement *element DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
FsioResult processElement(const LEElement* element DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
float pop(le_action_e action);
|
||||
LEElement *first;
|
||||
|
||||
LEElement* m_program = nullptr;
|
||||
|
||||
calc_stack_t stack;
|
||||
};
|
||||
|
||||
|
|
|
@ -559,9 +559,9 @@ static void showFsio(const char *msg, LEElement *element) {
|
|||
#if EFI_PROD_CODE || EFI_SIMULATOR
|
||||
if (msg != NULL)
|
||||
scheduleMsg(logger, "%s:", msg);
|
||||
while (element != NULL) {
|
||||
while (element->action != LE_METHOD_RETURN) {
|
||||
scheduleMsg(logger, "action %d: fValue=%.2f", element->action, element->fValue);
|
||||
element = element->next;
|
||||
element++;
|
||||
}
|
||||
scheduleMsg(logger, "<end>");
|
||||
#endif
|
||||
|
|
|
@ -44,7 +44,7 @@ FsioResult getEngineValue(le_action_e action DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(fsio, testParsing) {
|
||||
TEST(fsio, testTokenizer) {
|
||||
char buffer[64];
|
||||
|
||||
ASSERT_TRUE(strEqualCaseInsensitive("hello", "HELlo"));
|
||||
|
@ -64,29 +64,47 @@ TEST(fsio, testParsing) {
|
|||
|
||||
ASSERT_TRUE(isNumeric("123"));
|
||||
ASSERT_FALSE(isNumeric("a123"));
|
||||
}
|
||||
|
||||
TEST(fsio, testParsing) {
|
||||
LEElement thepool[TEST_POOL_SIZE];
|
||||
LEElementPool pool(thepool, TEST_POOL_SIZE);
|
||||
|
||||
LEElement *element;
|
||||
element = pool.parseExpression("1 3 AND not");
|
||||
LEElement *element = pool.parseExpression("1 3 AND not");
|
||||
ASSERT_TRUE(element != NULL);
|
||||
|
||||
ASSERT_EQ(element->action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(element->fValue, 1.0);
|
||||
ASSERT_EQ(element[0].action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(element[0].fValue, 1.0);
|
||||
|
||||
element = element->next;
|
||||
ASSERT_EQ(element->action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(element->fValue, 3.0);
|
||||
ASSERT_EQ(element[1].action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(element[1].fValue, 3.0);
|
||||
|
||||
element = element->next;
|
||||
ASSERT_EQ(element->action, LE_OPERATOR_AND);
|
||||
ASSERT_EQ(element[2].action, LE_OPERATOR_AND);
|
||||
|
||||
element = element->next;
|
||||
ASSERT_EQ(element->action, LE_OPERATOR_NOT);
|
||||
ASSERT_EQ(element[3].action, LE_OPERATOR_NOT);
|
||||
|
||||
element = element->next;
|
||||
ASSERT_TRUE(element == NULL);
|
||||
// last should be a return instruction
|
||||
ASSERT_EQ(element[4].action, LE_METHOD_RETURN);
|
||||
|
||||
ASSERT_EQ(pool.getSize(), 5);
|
||||
}
|
||||
|
||||
TEST(fsio, parsingMultiple) {
|
||||
LEElement poolArr[TEST_POOL_SIZE];
|
||||
LEElementPool pool(poolArr, TEST_POOL_SIZE);
|
||||
|
||||
LEElement* p1 = pool.parseExpression("2");
|
||||
ASSERT_EQ(p1[0].action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(p1[0].fValue, 2);
|
||||
ASSERT_EQ(p1[1].action, LE_METHOD_RETURN);
|
||||
|
||||
LEElement* p2 = pool.parseExpression("4");
|
||||
ASSERT_EQ(p2[0].action, LE_NUMERIC_VALUE);
|
||||
ASSERT_EQ(p2[0].fValue, 4);
|
||||
ASSERT_EQ(p2[1].action, LE_METHOD_RETURN);
|
||||
|
||||
// Check that they got allocated sequentially without overlap
|
||||
ASSERT_EQ(p2 - p1, 2);
|
||||
}
|
||||
|
||||
static void testExpression2(float selfValue, const char *line, float expected, Engine *engine) {
|
||||
|
@ -201,60 +219,6 @@ TEST(fsio, invalidFunction) {
|
|||
}
|
||||
|
||||
TEST(fsio, testLogicExpressions) {
|
||||
{
|
||||
|
||||
WITH_ENGINE_TEST_HELPER(FORD_INLINE_6_1995);
|
||||
|
||||
LECalculator c;
|
||||
|
||||
LEElement value1;
|
||||
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.0f);
|
||||
c.add(&value2);
|
||||
|
||||
LEElement value3;
|
||||
value3.init(LE_OPERATOR_AND);
|
||||
c.add(&value3);
|
||||
assertEqualsM("123 and 321", 1.0, c.getValue(0 PASS_ENGINE_PARAMETER_SUFFIX));
|
||||
|
||||
/**
|
||||
* fuel_pump = (time_since_boot < 4 seconds) OR (rpm > 0)
|
||||
* fuel_pump = time_since_boot 4 less rpm 0 > OR
|
||||
*/
|
||||
|
||||
c.reset();
|
||||
|
||||
LEElement thepool[TEST_POOL_SIZE];
|
||||
LEElementPool pool(thepool, TEST_POOL_SIZE);
|
||||
LEElement *e = pool.next();
|
||||
e->init(LE_METHOD_TIME_SINCE_BOOT);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_NUMERIC_VALUE, 4.0f);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_OPERATOR_LESS);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_METHOD_RPM);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_NUMERIC_VALUE, 0.0f);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_OPERATOR_MORE);
|
||||
|
||||
e = pool.next();
|
||||
e->init(LE_OPERATOR_OR);
|
||||
|
||||
pool.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* fan = (not fan && coolant > 90) OR (fan && coolant > 85)
|
||||
* fan = fan NOT coolant 90 AND more fan coolant 85 more AND OR
|
||||
|
|
Loading…
Reference in New Issue