From 11efd7132e46211a839b9c893656eaebdc114d13 Mon Sep 17 00:00:00 2001 From: Debajit Ghosh Date: Sun, 6 Oct 2024 17:30:22 -0700 Subject: [PATCH] reset rules when a higher priority rule triggers. allow the finished action for a rule to bump the newly action for that rule. --- .../java/com/team766/framework3/Rule.java | 4 + .../com/team766/framework3/RuleEngine.java | 16 +- .../team766/framework3/RuleEngineTest.java | 172 +++++++++++++++++- 3 files changed, 189 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/team766/framework3/Rule.java b/src/main/java/com/team766/framework3/Rule.java index c6f2ab0b..5999e7f9 100644 --- a/src/main/java/com/team766/framework3/Rule.java +++ b/src/main/java/com/team766/framework3/Rule.java @@ -157,6 +157,10 @@ public String getName() { return currentTriggerType; } + /* package */ void reset() { + currentTriggerType = TriggerType.NONE; + } + /* package */ void evaluate() { if (predicate.getAsBoolean()) { currentTriggerType = diff --git a/src/main/java/com/team766/framework3/RuleEngine.java b/src/main/java/com/team766/framework3/RuleEngine.java index 90cfa585..78d7ed3d 100644 --- a/src/main/java/com/team766/framework3/RuleEngine.java +++ b/src/main/java/com/team766/framework3/RuleEngine.java @@ -106,6 +106,7 @@ public final void run() { + "; mechanism " + mechanism.getName() + " already reserved by higher priority rule."); + rule.reset(); continue ruleLoop; } // see if a previously triggered rule is still using the mechanism @@ -123,11 +124,24 @@ public final void run() { log( Severity.INFO, "RULE CONFLICT! Ignoring rule: " - + rule + + rule.getName() + "; mechanism " + mechanism.getName() + " already being used in CommandScheduler by higher priority rule."); + rule.reset(); continue ruleLoop; + } else if (rule != existingRule) { + // new rule takes priority + // reset existing rule + log( + Severity.INFO, + "Pre-empting rule: " + + existingRule.getName() + + "; mechanism " + + mechanism.getName() + + " will now be reserved by higher priority rule " + + rule.getName()); + existingRule.reset(); } } } diff --git a/src/test/java/com/team766/framework3/RuleEngineTest.java b/src/test/java/com/team766/framework3/RuleEngineTest.java index bafcb50c..edb93b71 100644 --- a/src/test/java/com/team766/framework3/RuleEngineTest.java +++ b/src/test/java/com/team766/framework3/RuleEngineTest.java @@ -123,6 +123,45 @@ public void testRunNonConflictingRules() { step(); // 3 } + @Test + public void testFinishedProcedureBumpsNewlyProcedureForSameRule() { + RuleEngine myRules = + new RuleEngine() { + { + addRule( + Rule.create("fm1_p0", new PeriodicPredicate(4)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p0", 2, Set.of(fm1))) + .withFinishedTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procfin_p0", + 1, + Set.of(fm1, fm2)))); + } + }; + + myRules.run(); + + // check that the expected Procedure is scheduled + Command cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procnew_p0")); + + step(); // 0 + + // next iteration - check that the original procedure is new bumped by the finished procedure for the same rule + myRules.run(); + + cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procfin_p0")); + + step(); // 1 + } + @Test public void testRunRulePriorities() { // create simple RuleEngine with two rules with conflicting actions @@ -189,7 +228,6 @@ public void testRunRulePriorities() { @Test public void testRunHigherPriorityRuleStillBeingRun() { - // create a simple RuleEngine with two non-conflicting rules RuleEngine myRules = new RuleEngine() { { @@ -261,7 +299,6 @@ public void testRunHigherPriorityRuleStillBeingRun() { @Test public void testRunLowerPriorityRuleBumped() { - // create a simple RuleEngine with two non-conflicting rules RuleEngine myRules = new RuleEngine() { { @@ -301,4 +338,135 @@ public void testRunLowerPriorityRuleBumped() { step(); // 1 } + + @Test + public void testRuleResetIgnoredLowerPriorityRule() { + RuleEngine myRules = + new RuleEngine() { + { + addRule( + Rule.create("fm1_p0", new PeriodicPredicate(4)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p0", 2, Set.of(fm1)))); + addRule( + Rule.create("fm1_p1", new PeriodicPredicate(4)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p1", 1, Set.of(fm1))) + .withFinishedTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procfin_p1", 1, Set.of(fm2)))); + } + }; + + myRules.run(); + + // check that the expected Procedure is scheduled + Command cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procnew_p0")); + + step(); // 0 + + myRules.run(); + + cmd = CommandScheduler.getInstance().requiring(fm2); + assertNull(cmd); + + step(); + } + + @Test + public void testRuleResetIgnoredLowerPriorityRuleHigherPriorityRulePreviouslyScheduled() { + RuleEngine myRules = + new RuleEngine() { + { + addRule( + Rule.create("fm1_p0", new PeriodicPredicate(4)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p0", 2, Set.of(fm1)))); + addRule( + Rule.create("fm1_p1", new PeriodicPredicate(4, 1)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p1", 1, Set.of(fm1))) + .withFinishedTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procfin_p1", 1, Set.of(fm2)))); + } + }; + + myRules.run(); + + // check that the expected Procedure is scheduled + Command cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procnew_p0")); + + step(); // 0 + + // next iteration - even with the second rule firing, the procedure from the first rule + // should continue + // executing, since the first rule is higher priority + myRules.run(); + cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procnew_p0")); + step(); // 1 + + // next iteration - the second rule's finished procedure should *not* execute + myRules.run(); + cmd = CommandScheduler.getInstance().requiring(fm2); + assertNull(cmd); + step(); // 2 + } + + @Test + public void testRuleResetBumpedLowerPriorityRule() { + RuleEngine myRules = + new RuleEngine() { + { + addRule( + Rule.create("fm1_p0", new PeriodicPredicate(4, 1)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p0", 2, Set.of(fm1)))); + addRule( + Rule.create("fm1_p1", new PeriodicPredicate(4)) + .withNewlyTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procnew_p1", 2, Set.of(fm1))) + .withFinishedTriggeringProcedure( + () -> + new FakeProcedure( + "fm1procfin_p1", 2, Set.of(fm2)))); + } + }; + + myRules.run(); + + // check that the expected Procedure is scheduled + Command cmd = CommandScheduler.getInstance().requiring(fm1); + assertNotNull(cmd); + assertTrue(cmd.getName().endsWith("fm1procnew_p1")); + + step(); // 0 + + myRules.run(); + + cmd = CommandScheduler.getInstance().requiring(fm2); + assertNull(cmd); + + step(); + } }