Skip to content

Commit

Permalink
Added negated triggers and blockers
Browse files Browse the repository at this point in the history
  • Loading branch information
krichardsson committed May 15, 2023
1 parent f4d0ad2 commit 5f35cff
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 10 deletions.
2 changes: 2 additions & 0 deletions docs/functional-areas/supervisor/conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ All conditions are collected in a bit field that expresses the full state of the
that are relevant to the supervisor.

All conditions can be found in the `src/modules/interface/supervisor_state_machine.h` file.

The condition bit field is used to trigger [state transitions](transitions.md).
4 changes: 2 additions & 2 deletions docs/functional-areas/supervisor/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ situations or for protection.

The update sequence is separated into a few steps:
1. Collect data from sensors and the system. We call these [conditions](conditions.md).
2. Based on the conditions, check if the state machine should transition into a new [state](states.md)
3. If there is a state transition, possibly execute one ore more actions
2. Based on the conditions, check if the state machine should [transition](transitions.md) into a new [state](states.md)
3. If there is a state [transition](transitions.md), possibly execute one ore more actions
4. Set the new state

## Modifying behavior
Expand Down
30 changes: 30 additions & 0 deletions docs/functional-areas/supervisor/transitions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: Supervisor transitions
page_id: supervisor_transitions
---

A state transition takes the state machine from one state to another. The [conditions bit field](conditions.md) is used to identify
which, if any transition to trigger. Transitions are defined as a list per state and are evaluated in order. If a
transition is triggered, the evaluation is terminated and no further transitions will be checked. That means
that the order of a transition list sets the priority, two transitions might have settings that makes both valid, but
the first one will "win".

A state transition is defined with a bit field of triggers. The bits in the trigger bit field are compared to the bits
in the current [conditions bit field](conditions.md) to see if they are set. There is also a bit field with negated triggers where the
bits in the condition bit field should be zero to trigger. To tie it together there is a trigger combiner that describes
the logical operation to use to decide whether the state transition should be triggered or not
* `supervisorAll` = all `trigger` bits must be set and all `negated trigger` bits must be zero in the condition bit field.
* `supervisorAny` = at least one `trigger` bit must be set or at least one `negated trigger` bits must be zero
* `supervisorAlways` = ignore the condition bit field and treat the condition as true
* `supervisorNever` = ignore the condition bit field and treat the condition as false

The second part of a state transition definition is a blocking functionality that works the same way as a trigger, but
is negated. It contains a bit field called blockers, a second bit field called negated blocker and a blocker combiner.
If the blocker evaluates to true, the trigger will not happen.

The final data of a state transition definition is the new state that the state machine will enter, if the trigger
condition is met and the blocking condition is not met.

The system of state transition definitions with triggers, negated triggers, blockers and negated blockers, match with
combiners provides a high degree of freedom. Note that it is possible to express a set of state transitions with trigger
conditions in multiple ways.
15 changes: 7 additions & 8 deletions src/modules/src/supervisor_state_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,15 @@ static bool isAnySet(const supervisorConditionBits_t conditions, const superviso
return (conditions & requirements) != 0;
}

static bool areConditionsMet(const supervisorConditionBits_t conditions, const supervisorConditionBits_t requirements, const SupervisorConditionCombiner_t combiner) {
static bool areConditionsMet(const supervisorConditionBits_t conditions, const supervisorConditionBits_t requirements, const supervisorConditionBits_t negRequirements, const SupervisorConditionCombiner_t combiner) {
bool result = false;

switch(combiner) {
case supervisorAll:
result = areAllSet(conditions, requirements);
result = areAllSet(conditions, requirements) && !isAnySet(conditions, negRequirements);
break;
case supervisorAny:
result = isAnySet(conditions, requirements);
result = isAnySet(conditions, requirements) || !areAllSet(conditions, negRequirements);
break;
case supervisorAlways:
result = true;
Expand All @@ -263,12 +263,11 @@ TESTABLE_STATIC supervisorState_t findTransition(const supervisorState_t current
for (int i = 0; i < transitions->length; i++) {
const SupervisorStateTransition_t* transitionDef = &transitions->transitionList[i];

const bool triggerConditionsMet = areConditionsMet(conditions, transitionDef->triggers, transitionDef->triggerCombiner);
const bool blockerConditionsMet = areConditionsMet(conditions, transitionDef->blockers, transitionDef->blockerCombiner);
const bool isTriggerMatch = areConditionsMet(conditions, transitionDef->triggers, transitionDef->negatedTriggers, transitionDef->triggerCombiner);
const bool isBlockerMatch = areConditionsMet(conditions, transitionDef->blockers, transitionDef->negatedBlockers, transitionDef->blockerCombiner);

const bool conditionsMet = triggerConditionsMet && !blockerConditionsMet;

if (conditionsMet) {
const bool isStateTransitionValid = isTriggerMatch && !isBlockerMatch;
if (isStateTransitionValid) {
newState = transitionDef->newState;
break;
}
Expand Down
238 changes: 238 additions & 0 deletions test/modules/src/test_supervisor_state_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,40 @@ void testTransitionOneRequiredConditionNotMet(void) {
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionOneNegatedRequiredConditionNotMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionOneNegatedRequiredConditionMet(void) {
// Fixture
supervisorConditionBits_t conditions = 0;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiRequiredConditionsMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
Expand Down Expand Up @@ -179,6 +213,142 @@ void testTransitionMultiRequiredConditionsOneMet(void) {
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegatedRequiredConditionOneNotMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_ARMED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegatedRequiredConditionsMet(void) {
// Fixture
supervisorConditionBits_t conditions = 0;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedRequiredConditionsAllMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedRequiredConditionsOnePositiveNotMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedRequiredConditionsOneNegativeNotMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED | SUPERVISOR_CB_CHARGER_CONNECTED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAll;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedOnePositiveRequirementMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_IS_TUMBLED | SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED;
SupervisorConditionCombiner_t triggerCombiner = supervisorAny;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedNoRequirementMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED | SUPERVISOR_CB_COMMANDER_WDT_WARNING;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED | SUPERVISOR_CB_COMMANDER_WDT_WARNING;
SupervisorConditionCombiner_t triggerCombiner = supervisorAny;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiMixedOneNegativeRequirementMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_COMMANDER_WDT_WARNING;

supervisorConditionBits_t triggers = SUPERVISOR_CB_COMMANDER_WDT_TIMEOUT | SUPERVISOR_CB_IS_TUMBLED;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_ARMED | SUPERVISOR_CB_COMMANDER_WDT_WARNING;
SupervisorConditionCombiner_t triggerCombiner = supervisorAny;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t blockerCombiner = supervisorNever;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiRequiredConditionsOneMissingButOtherBitsSet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;
Expand Down Expand Up @@ -315,6 +485,74 @@ void testTransitionMultiProhibitedConditionsAllMet(void) {
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegativeProhibitedConditionsNoneMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t triggerCombiner = supervisorAlways;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;
SupervisorConditionCombiner_t blockerCombiner = supervisorAny;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegativeProhibitedConditionsOneMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_IS_TUMBLED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t triggerCombiner = supervisorAlways;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;
SupervisorConditionCombiner_t blockerCombiner = supervisorAny;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegativeProhibitedConditionsOneNotMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_IS_TUMBLED;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t triggerCombiner = supervisorAlways;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;
SupervisorConditionCombiner_t blockerCombiner = supervisorAll;

// Test
// Assert
assertStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiNegativeProhibitedConditionsAllMet(void) {
// Fixture
supervisorConditionBits_t conditions = 0;

supervisorConditionBits_t triggers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedTriggers = SUPERVISOR_CB_NONE;
SupervisorConditionCombiner_t triggerCombiner = supervisorAlways;

supervisorConditionBits_t blockers = SUPERVISOR_CB_NONE;
supervisorConditionBits_t negatedBlockers = SUPERVISOR_CB_CHARGER_CONNECTED | SUPERVISOR_CB_IS_TUMBLED;
SupervisorConditionCombiner_t blockerCombiner = supervisorAll;

// Test
// Assert
assertNoStateTransition(conditions, triggers, negatedTriggers, triggerCombiner, blockers, negatedBlockers, blockerCombiner);
}

void testTransitionMultiRequiredAndProhibitedConditionsMet(void) {
// Fixture
supervisorConditionBits_t conditions = SUPERVISOR_CB_ARMED | SUPERVISOR_CB_IS_TUMBLED;
Expand Down

0 comments on commit 5f35cff

Please sign in to comment.