// fixme: Not sure yet how to handle real-time issues. Don't want this loop to spin endlessly, will need it to yield so other processes // can run. For a simple lexeme-based CSA, the presenter could be where the yield lives. Another option is to not have Execute() be a loop. // Instead use an event-based architecture where changes to the blackboard trigger the controller. For now, just select a single KS and // execute it. Punt looping to the enclosing application. public void Execute() { UpdateAgenda(); IKnowledgeSourceActivation selectedKSA = SelectKSForExecution(); if (selectedKSA != null) { selectedKSA.Execute(); m_Agenda.Remove(selectedKSA); } }
// fixme: selects the highest priority KSA for execution. More generally, would want to select KSs probabilistically based on priority. // Consider implementing controllers with KSAs on their own blackboard. There's a regress where the KSs that implement a controller need // their own meta-controller to decide what to do. This regress can be broken by having "eager" KSs that execute immediately when their // preconditions are satisfied (like a forward-chaining rule). protected override IKnowledgeSourceActivation SelectKSForExecution() { IKnowledgeSourceActivation highestPriorityKSA = null; int highestPriority = int.MinValue; foreach (IKnowledgeSourceActivation KSA in m_Agenda) { Debug.Assert(KSA.Properties.ContainsKey(Priority)); int curPriority = (int)KSA.Properties[Priority]; if (curPriority > highestPriority) { highestPriorityKSA = KSA; highestPriority = curPriority; } } return(highestPriorityKSA); // returns null if there are no KSs in the agenda }
public override IKnowledgeSourceActivation[] Precondition() { // Use LINQ to create a collection of the requested U_PrologEvalQueries on the blackboard. var requests = from request in m_blackboard.LookupUnits <U_PrologEvalRequest>() // Lookup ID select requests where // where the request has not been previously matched by this knowledge source precondition (!request.Slots.ContainsKey(KSPreconditionMatched)) || (!((ISet <ReactiveKnowledgeSource>)request.Slots[KSPreconditionMatched]).Contains(this)) select request; IKnowledgeSourceActivation[] activations = new IKnowledgeSourceActivation[requests.Count()]; // Currently only support one prolog KB // fixme: eventually may want to support multiple prolog KBs so will need mechanism for supporting them as well as inheritance var prologKB = m_blackboard.LookupSingleton <U_PrologKB>(); // Lookup prolog kbs. // Iterate through each of the requests, creating KnowledgeSourceActivations int i = 0; foreach (var request in requests) { var boundVars = new Dictionary <string, object> { [PrologEvalRequest] = request, [PrologKB] = prologKB }; activations[i++] = new KnowledgeSourceActivation(this, boundVars); // fixme: this bit of boilerplate code for marking a knowledge unit as having already participated in a matched precondition // should be baked into the infrastructure somewhere so knowledge source implementers don't always have to do this. // A good place to add this would be on Unit (and declare a method on IUnit). if (request.Slots.ContainsKey(KSPreconditionMatched)) { ((HashSet <ReactiveKnowledgeSource>)request.Slots[KSPreconditionMatched]).Add(this); } else { request.Slots[KSPreconditionMatched] = new HashSet <ReactiveKnowledgeSource> { this }; } } return(activations); }
public override IKnowledgeSourceActivation[] Precondition() { // Use LINQ to create a collection of the requested U_IDQueries on the blackboard. var requests = from request in m_blackboard.LookupUnits <U_IDSelectRequest>() // Lookup ID queries where // where the query has not been previously matched by this knowledge source precondition (!request.Slots.ContainsKey(KSPreconditionMatched)) || (!((ISet <ReactiveKnowledgeSource>)request.Slots[KSPreconditionMatched]).Contains(this)) select request; IKnowledgeSourceActivation[] activations = new IKnowledgeSourceActivation[requests.Count()]; // Iterate through each of the queries, creating KnowledgeSourceActivations int i = 0; foreach (var request in requests) { var boundVars = new Dictionary <string, object> { [IDSelectRequest] = request }; activations[i++] = new KnowledgeSourceActivation(this, boundVars); // fixme: this bit of boilerplate code for marking a knowledge unit as having already participated in a matched precondition // should be baked into the infrastructure somewhere so knowledge source implementers don't always have to do this. // A good place to add this would be on Unit (and declare a method on IUnit). if (request.Slots.ContainsKey(KSPreconditionMatched)) { ((HashSet <ReactiveKnowledgeSource>)request.Slots[KSPreconditionMatched]).Add(this); } else { request.Slots[KSPreconditionMatched] = new HashSet <ReactiveKnowledgeSource> { this }; } } return(activations); }
/*public*/ void TestSelectKSForExecution_PriorityController() { PriorityController_PublicMethods controller = new PriorityController_PublicMethods(); IBlackboard blackboard = new Blackboard(); KS_Old_ReactiveIDSelector ks1 = new KS_Old_ReactiveIDSelector(blackboard); KS_Old_ReactiveIDSelector ks2 = new KS_Old_ReactiveIDSelector(blackboard); KS_Old_ReactiveIDSelector ks3 = new KS_Old_ReactiveIDSelector(blackboard); ks1.Properties[Priority] = 10; ks2.Properties[Priority] = 30; ks3.Properties[Priority] = 20; controller.AddKnowledgeSource(ks1); controller.AddKnowledgeSource(ks2); controller.AddKnowledgeSource(ks3); blackboard.AddUnit(new U_IDSelectRequest("foo")); controller.UpdateAgenda(); Assert.Equal(3, controller.Agenda.Count); IKnowledgeSourceActivation KSA = controller.SelectKSForExecution(); Assert.Equal(30, KSA.Properties[KSProps.Priority]); }