// ExecuteWithContext function will execute a knowledge evaluation and action against data context.
// The engine will evaluate context cancelation status in each cycle.
// The engine also do conflict resolution of which rule to execute.
ExecuteWithContext function will execute a knowledge evaluation and action against data context.
The engine will evaluate context cancelation status in each cycle.
The engine also do conflict resolution of which rule to execute.
// ExecuteWithContext function will execute a knowledge evaluation and action against data context . // The engine will evaluate context cancelation status in each cycle . // The engine also do conflict resolution of which rule to execute .
func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) error {
	log.Debugf("Starting rule execution using knowledge '%s' version %s. Contains %d rule entries", knowledge.Name, knowledge.Version, len(knowledge.RuleEntries))
	startTime := time.Now()
	defunc := &ast.BuiltInFunctions{
		Knowledge:     knowledge,
		WorkingMemory: knowledge.WorkingMemory,
		DataContext:   dataCtx,
	}
	dataCtx.Add("DEFUNC", defunc)
	log.Debugf("Resetting Working memory")
	knowledge.WorkingMemory.ResetAll()
	log.Debugf("Initializing Context")
	knowledge.InitializeContext(dataCtx)
	var cycle uint64
	for {
		if ctx.Err() != nil {
			log.Error("Context canceled")
			return ctx.Err()
		}
		log.Tracef("Select all rule entry that can be executed.")
		runnable := make([]*ast.RuleEntry, 0)
		for _, v := range knowledge.RuleEntries {
			can, err := v.Evaluate(dataCtx, knowledge.WorkingMemory)
			if err != nil {
				log.Errorf("Failed testing condition for rule : %s. Got error %v", v.RuleName, err)
			}
			if can {
				runnable = append(runnable, v)
			}
		}
		log.Tracef("Selected rules %d.", len(runnable))
		if len(runnable) > 0 {
			cycle++
			log.Debugf("Cycle #%d", cycle)
			if cycle > g.MaxCycle {
				log.Error("Max cycle reached")
				return fmt.Errorf("the GruleEngine successfully selected rule candidate for execution after %d cycles, this could possibly caused by rule entry(s) that keep added into execution pool but when executed it does not change any data in context. Please evaluate your rule entries \"When\" and \"Then\" scope. You can adjust the maximum cycle using GruleEngine.MaxCycle variable", g.MaxCycle)
			}
			runner := runnable[0]
			if len(runnable) > 1 {
				for idx, pr := range runnable {
					if idx > 0 && runner.Salience < pr.Salience {
						runner = pr
					}
				}
			}
			err := runner.Execute(dataCtx, knowledge.WorkingMemory)
			if err != nil {
				log.Errorf("Failed execution rule : %s. Got error %v", runner.RuleName, err)
				return err
			}
			if dataCtx.IsComplete() {
				break
			}
		} else {
			log.Debugf("No more rule to run")
			break
		}
	}
	log.Debugf("Finished Rules execution. With knowledge base '%s' version %s. Total #%d cycles. Duration %d ms.", knowledge.Name, knowledge.Version, cycle, time.Now().Sub(startTime).Nanoseconds()/1e6)
	return nil
}
func ( g * GruleEngine ) ExecuteWithContext ( ctx context . Context , dataCtx ast . IDataContext , knowledge * ast . KnowledgeBase ) error { log . Debugf ( " " , knowledge . Name , knowledge . Version , len ( knowledge . RuleEntries ) ) startTime := time . Now ( ) defunc := & ast . BuiltInFunctions { Knowledge : knowledge , WorkingMemory : knowledge . WorkingMemory , DataContext : dataCtx , } dataCtx . Add ( " " , defunc ) log . Debugf ( " " ) knowledge . WorkingMemory . ResetAll ( ) log . Debugf ( " " ) knowledge . InitializeContext ( dataCtx ) var cycle uint64 for { if ctx . Err ( ) != nil { log . Error ( " " ) return ctx . Err ( ) } log . Tracef ( " " ) runnable := make ( [ ] * ast . RuleEntry , 0 ) for _ , v := range knowledge . RuleEntries { can , err := v . Evaluate ( dataCtx , knowledge . WorkingMemory ) if err != nil { log . Errorf ( " " , v . RuleName , err ) } if can { runnable = append ( runnable , v ) } } log . Tracef ( " " , len ( runnable ) ) if len ( runnable ) > 0 { cycle ++ log . Debugf ( " " , cycle ) if cycle > g . MaxCycle { log . Error ( " " ) return fmt . Errorf ( " \" \" \" \" " , g . MaxCycle ) } runner := runnable [ 0 ] if len ( runnable ) > 1 { for idx , pr := range runnable { if idx > 0 && runner . Salience < pr . Salience { runner = pr } } } err := runner . Execute ( dataCtx , knowledge . WorkingMemory ) if err != nil { log . Errorf ( " " , runner . RuleName , err ) return err } if dataCtx . IsComplete ( ) { break } } else { log . Debugf ( " " ) break } } log . Debugf ( " " , knowledge . Name , knowledge . Version , cycle , time . Now ( ) . Sub ( startTime ) . Nanoseconds ( ) / 1e6 ) return nil }
ExecuteWithContext function will execute a knowledge evaluation and action against data context.
ExecuteWithContext function will execute a knowledge evaluation and action against data context .
// Prepare the timer, we need to measure the processing time in debug mode. // Prepare the build-in function and add to datacontext. // Working memory need to be resetted. all Expression will be set as not evaluated. // Initialize all AST with datacontext and working memory /*
		Un-limited loop as long as there are rule to execute.
		We need to add safety mechanism to detect unlimited loop as there are possibility executed rule are not changing
		data context which makes rules to get executed again and again.
	*/ // Select all rule entry that can be executed. // test if this rule entry v can execute. // No longer return error, since unavailability of variable or fact in context might be intentional. // if can, add into runnable array // disabled to test the rete's variable change detection. // knowledge.RuleContextReset() // If there are rules to execute, sort them by their Salience // add the cycle counter // if cycle is above the maximum allowed cycle, returnan error indicated the cycle has ended. // execute the top most prioritized rule // scan all runnables and pick the highest salience // execute the top most prioritized rule // No more rule can be executed, so we are done here.